Sean
Sean

Reputation: 370

Angular parent component listen to a ng-content child component

I want a function to trigger within my parent component when my child component emits an event. The child component is passed into the parent through ng-content. I simplified the code of what I'm trying to do but I can't seem to have the child emit to the parent. I want to pass the child with ng-content because the child component can be a variety of components and not 1 single specific one

Example:

main.ts

<parent-component>
  <child-component></child-component>
</parent-component>

parent.component.ts

<div>
 {{ this.value }}
 <ng-content (updated)="updateParentValue()"></ng-content> <-- this doesn't work
</div>

updateParentValue(value) {
  this.value = value;
}

child.component.ts

<div>
  <span (click)="updateStuff()">update</span>
</div>

@Output() updated = new EventEmitter();

updateStuff() {
  // Stuff
  this.updated.emit(this.value);
}

Upvotes: 2

Views: 2535

Answers (2)

Maxime Lyakhov
Maxime Lyakhov

Reputation: 391

The only answer is not managing subscriptions. Here I'm using gorgeous @rx-angular/state with connect method to manage subscription automatically, but you can manage it manually or use @ngneat/until-destroy.

ngAfterViewInit(): void {
  const tabChanges$ = this.tabs.changes.pipe(
    tap((tabs: QueryList<TabComponent>) => tabs),
    tap((tabs) => tabs.forEach((tab, index) => (tab.index = index))),
    switchMap((tabs) =>
      merge(...tabs.map((tab, index) => tab.clicked.pipe(map(() => index))))
    ),
    startWith(0),
    tap((index) => this.setActiveTab(index)),
    map((index) => ({index}))
  );
  this.state.connect(tabChanges$);

  // start tracking tabs
  this.tabs.notifyOnChanges();
  this.tabContents.notifyOnChanges();
}
  • tap to cast type since QueryList.changes outputs
  • tap again to assign values to children
  • switchMap to react to children change
    • merge with rested(...) array to subscribe to every component
  • startWith to have default value when nothing is clicked yet

Upvotes: 0

Sean
Sean

Reputation: 370

Thanks Krim, solved it with that link

main.ts

<parent-component>
  <child-component></child-component>
</parent-component>

parent.component.ts

<div>
 {{ this.value }}
 <ng-content></ng-content>
</div>

@ContentChildren(ChildComponent) childComponents: QueryList<ChildComponent>;

ngAfterViewInit() {
   this.childComponents.forEach((childComponent: ChildComponent) => {
       childComponent.updated.subscribe((item) => this.updateParentValue(item));           
   });
}

updateParentValue(value) {
  this.value = value;
}

child.component.ts

<div>
  <span (click)="updateStuff()">update</span>
</div>

@Output() updated = new EventEmitter();

updateStuff() {
  // Stuff
  this.updated.emit(this.value);
}

Upvotes: 4

Related Questions