AndreaNobili
AndreaNobili

Reputation: 42957

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:

enter image description here

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?

Upvotes: 1

Views: 860

Answers (3)

Geeks Trick
Geeks Trick

Reputation: 11

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);
  }
}

Upvotes: 1

Muhammed Yusuf
Muhammed Yusuf

Reputation: 156

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

Upvotes: 0

Owen Kelvin
Owen Kelvin

Reputation: 15083

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

Upvotes: 2

Related Questions