简体   繁体   中英

How can I correctly pass a value from a second level sub component to the parent component in this situation?

I am not so into Angular and TypeScript (I came from Java) and I have the following problem to solve. I don't know if the solution that I think to use is correct or if there are bettere solutions.

Basically I have a component named order-manager.component that is a parent component rendering this tables:

在此处输入图片说明

This parent component contains the list of object representing a single row:

orders: any[];

and into the ngOnInit() retrieve the list using a service.

As you can see in the previous printscreen each rows of my table can be expanded in order to show\\edit the details of the specific object.

So to do this I used a sub component named order-details . So basically in my parent component HTML I have something like this:

DETTAGLIO ORDINE

This order-details subcomponent have 2 subcomponents itself: one for the view mode and the second one for the edit mode (hightlited in the previous screenshot).

So basically into my code of this order-details component I simply have:

 <p-selectButton [options]="editOrderOption" [(ngModel)]="editOrderSelectedOption" (onChange)="editOrderOptionOnChange($event, orderDetail.id)"></p-selectButton> <div *ngIf="editOrderSelectedOption=='view';then view_content else edit_content">here is ignored</div> <ng-template #view_content> <app-view-order [orderDetail]="orderDetail"></app-view-order> </ng-template> <ng-template #edit_content> <app-update-order [orderDetail]="orderDetail"></app-update-order> </ng-template>

Basically the user choose the view or edit mode by the select button and a sub component is rendered in the page.

In the specific case we are into edit mode using this update-order component.

As you can see in the previous image this update-order component allow the user to edit the form and at the end it contains the Delete button. Clicking this button I want to delete the object representing this order (this row of the table) from my table.

And here my problem. The list of object representing the table rows is defined in the first order-manager.component parent component and the button is defined into the second level update-order sub component, this is the hierarchy:

 order-manager component | | |---------> order-details component | | |----------------> update-order component

To solve this problem I was thinking that I can do something like this:

  1. The user click on the Delete button defined into update-order sub component. This is handled by a method that emit an event containing the ID of the current row (it is the unique value of the object field.

  2. Into the order-manager parent component I listen for this event. When the event is received it is removed from the orders list representing the list of rows of my table.

Do you think that it is a pretty decent and neat solution to achieve this task or am I missing something and there are better solutions?

Emitting a value will work.

A simpler solution will be to create a service that may track user actions. Create a Service say OrderChangeService and inject this on both order-manager component and update-order component

export class OrderChangeService {
  deleteIdSubject$ = new Subject<number>(); // import { Subject } from rxjs
  deleteIdAction$ = deleteIdSubject$.asObservable()
}

Now in update-order component when a user clicks to delete a specific order, you call the next() function on the subject

  deleteOrder(id: number) {
    deleteIdSubject$.next(id);
  }

In your order manager component you can now subscribe to deleteIdAction$ in your ngOnInit() function

deleteIdAction$ = this.orderChangeService.deleteIdAction$ // make sure you inject service in the constructor
  ngOnInit() {
    this.deleteIdAction$.subscribe({
      next: (id) => {
        // Do Delete action for item with id
      }
    })
  }

The basic idea is that a service can be used to pass information from one component to another. Emitting values may be problematic as the depth of nested components increases

The Best approach would actually be to use NgRx for state management. May be a bit difficult to use but produces better result. You may have a look at the official documentation of NgRx

Instead, you can create a data service which updates and fetch the data through it

import { Injectable } from '@angular/core';
import { Subject, Observable } from 'rxjs';
@Injectable()
export class MessageService {
  private siblingMsg = new Subject<string>();
  constructor() { }
  /*
   * @return {Observable<string>} : siblingMsg
   */
  public getMessage(): Observable<string> {
    return this.siblingMsg.asObservable();
  }
  /*
   * @param {string} message : siblingMsg
   */
  public updateMessage(message: string): void {
    this.siblingMsg.next(message);
  }
}

and from the component, you can set the value using subscribe

import { Component, OnInit, OnDestroy } from '@angular/core';
import { MessageService } from './message.service';
...
export class AppComponent implements OnInit{
  public messageForSibling: string;
  public subscription: Subscription;
  constructor(
    private msgservice: MessageService // inject service
  ) {}

  public ngOnDestroy(): void {
    this.subscription.unsubscribe(); // onDestroy cancels the subscribe request
  }

  public ngOnInit(): void {
    // set subscribe to message service
    this.subscription = this.messageService.getMessage().subscribe(msg => this.messageForSibling = msg);
  }
}

At first, you should create an output property in your child component.

  @Output() 
  updated = new EventEmitter<boolean>();
  
  saveButtonClicked() {
    
    //do update
    
    this.updated.emit(true); //update success
    
  }

Define it in your html:

<ng-template #edit_content>
  <app-update-order [orderDetail]="orderDetail" (updated)="orderUpdated($event)"></app-update-order>
</ng-template>

Now, you can listen update events in parent component.

orderUpdated(updated:boolean){
    if(updated){
    
    }
}

You can find more information about component interaction here

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM