If I have this components hierarchy in Angular: enter image description here

And there is something happening in component F that B should know about. One way I can do it is using emit() in F and pass it along the way up to A, but this will require me to emit event in each component E,D and C, even though they don't care and should not care about the data being emitted - So in F I will write

export class F .... {
  ...

  @Output() emitterName: EventEmitter<any> = new EventEmitter<any>();

  doSomething(data) {
   ...
   this.emitterName.emit(data);
  }
  ...
}

Then, class E should call F like <app-f-selector (emitterName)="setData($event)"><app-f-selector/>

And in E, I should create a new function setData() that just emits the event up to D:

export class E .... {
  ...

  @Output() emitterName: EventEmitter<any> = new EventEmitter<any>();

  setData(data) {
   ...
   this.emitterName.emit(data);
  }
  ...
}

Now again I should do the same for D and C. That looks really bad, is there some elegant way in Angular to pass data from children component up to parent component, when there are n children along the way without code duplication? Something like React context?

3

There are 3 best solutions below

0
On BEST ANSWER

Something like this (not checked, but as a guide):

export class MySharedService {

  private data$ = new Subject<any>();
  public setData(data: any) {
      this.data$.next(data);
  }
  public getData(): Observable<any> {
      return this.data$.asObservable();
  }
}

then in any component that requires this data, inject via constructor and subscribe to changes:

this.mySharedService.getData().subscribe(data => ...do something here);
0
On

If you're sure that you may have only one parent with type "A", you can inject in the children and call it directly.

export class F {
  constructor(
    private parentA: AComponent
  ){}

  setData(data) {
   this.parentA.doSomething(data);
  }
}

But I would not recommend such a structure, since this makes component F tightly coupled to component A. In our project, we use this approach only to implement custom UI components, which are dynamically generated.

0
On

You can do it using share service ie.

// shared.service.ts
export class SharedService  {
  private updateComp = new Subject<any>();
  public updateComp$ = this.updateComp.asObservable();
    
    public updateComponent(data: any) {
    this.updateComp.next(data);
  }
}

// C OR D Component .ts enject sharedService in constructor
ngOnInit() {
    this.sharedService.updateComp$.subscribe((res: any) => {
    //you code...
    });
}

// F Component .ts call share service method i.e.
this.sharedService.updateComponent({ you data});