Child component events passed to the parent

I would like to implement a generic Angular 1.x template that has child directives in the parent directive in Angular 2. Here is my desired structure.

<foo> <bar>A</bar> <bar>B</bar> <bar>C</bar> </foo> 

I would like these Bar components to have click events that are dispatched to the Foo component.

Here is my Foo now:

 @Component({ selector: 'foo', template: ' <div> <ng-content></ng-content> </div> ' }) export class Foo { @ContentChildren(Bar) items: QueryList<Bar>; } 

And here is my Bar :

 @Component({ selector: 'Bar', template: ' <div (click)="clickity()"> <ng-content></ng-content> </div> ' }) export class Bar { clickity() { console.log('Broadcast this to the parent please!'); } } 

How can I notify Foo each of his Bars ?

+5
source share
3 answers

Another answer does a very poor job of solving the problem. EventEmitters are for use only in conjunction with @Outputs , as well as this problem not using dependency injection built into Angular 2 or RxJS functions.

In particular, without using DI, you force yourself into a scenario where, if you reuse components that depend on a static class, they will all receive the same events that you probably do not need.

Please take a look at the example below, using DI, it is easy to provide the same class several times, allowing for more flexible use, and also avoiding the need for fun naming schemes. If you need multiple events, you can provide multiple versions of this simple class using opaque tokens.

Working example: http://plnkr.co/edit/RBfa1GKeUdHtmzjFRBLm?p=preview

 // The service import 'rxjs/Rx'; import {Subject,Subscription} from 'rxjs/Rx'; export class EmitterService { private events = new Subject(); subscribe (next,error,complete): Subscriber { return this.events.subscribe(next,error,complete); } next (event) { this.events.next(event); } } @Component({ selector: 'bar', template: ` <button (click)="clickity()">click me</button> ` }) export class Bar { constructor(private emitter: EmitterService) {} clickity() { this.emitter.next('Broadcast this to the parent please!'); } } @Component({ selector: 'foo', template: ` <div [ngStyle]="styl"> <ng-content></ng-content> </div> `, providers: [EmitterService], directives: [Bar] }) export class Foo { styl = {}; private subscription; constructor(private emitter: EmitterService) { this.subscription = this.emitter.subscribe(msg => { this.styl = (this.styl.background == 'green') ? {'background': 'orange'} : {'background': 'green'}; }); } // Makes sure we don't have a memory leak by destroying the // Subscription when our component is destroyed ngOnDestroy() { this.subscription.unsubscribe(); } } 
+5
source

You can use the service to send data between components if you cannot do this using @Output() decorator. Here is an example:

 import {EventEmitter} from 'angular2/core'; export class EmitterService { private static _emitters: { [channel: string]: EventEmitter<any> } = {}; static get(channel: string): EventEmitter<any> { if (!this._emitters[channel]) this._emitters[channel] = new EventEmitter(); return this._emitters[channel]; } } 

You import it wherever you need to release or subscribe to an event:

 // foo.component.ts import {EmitterService} from '../path/to/emitter.service' class Foo { EmitterService.get("some_id").subscribe(data => console.log("some_id channel: ", data)); EmitterService.get("other_id").subscribe(data => console.log("other_id channel: ", data)); } // bar.component.ts import {EmitterService} from '../path/to/emitter.service' class Bar { onClick() { EmitterService.get("some_id").emit('you clicked!'); } onScroll() { EmitterService.get("other_id").emit('you scrolled!'); } } 

another example: plunker

+14
source

Why not use @ContentChildern?

in bar.component.ts we expose the clicked event

 @Output() clicked = new EventEmitter<BarComponent>(); onClick(){ this.clicked.emit(this); } 

at foo.component.ts we subscribe to each clicked event

  @ContentChildren(BarComponent) accordionComponents: QueryList<BarComponent>; ngAfterViewInit() { this.accordionComponents.forEach((barComponent: BarComponent) => { barComponent.clicked.subscribe((bar: BarComponent) => doActionsOnBar(bar)); }); } 
+3
source

Source: https://habr.com/ru/post/1240653/


All Articles