web-dev-qa-db-ja.com

親にブロードキャストされる子コンポーネントイベント

Angular 2。の親ディレクティブ内に子ディレクティブを持つ一般的なAngular 1.xパターンを実装したいと思います。2。これが私の望ましい構造です。

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

これらのBarコンポーネントにclickコンポーネントに発行されるFooイベントを持たせたいのですが。

これが私のこれまでのFooです:

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

そして、これが私の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!');
  }
}

FooのいずれかがクリックされるたびにBarsに通知するにはどうすればよいですか?

5
Jack Guy

もう1つの答えは、問題を解決するという非常に貧弱な仕事です。 EventEmittersは、@Outputsと組み合わせて使用​​することのみを目的としており、この問題は、Angular 2またはRxJSの機能に組み込まれている依存性注入を利用していません。

具体的には、DIを使用しないことで、静的クラスに依存するコンポーネントを再利用すると、それらすべてが同じイベントを受け取るというシナリオに陥ることになります。これはおそらく望ましくありません。

以下の例をご覧ください。DIを利用することで、同じクラスを複数回提供することが簡単になり、より柔軟に使用できるようになり、面白い命名スキームが不要になります。複数のイベントが必要な場合は、不透明なトークンを使用して、この単純なクラスの複数のバージョンを提供できます。

実例: 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
Mike Lovelace

@Output()デコレータを使用してデータを送信できない場合は、サービスを使用してコンポーネント間でデータを送信できます。次に例を示します。

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

イベントを発行またはサブスクライブする必要がある場所にインポートします。

// 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!');
  }
}

別の例: プランカー

14
Sasxa

@ContentChildernを使用しないのはなぜですか?

bar.component.tsで、クリックされたイベントを公開します

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

foo.component.tsでは、それぞれのクリックされたイベントをサブスクライブします

 @ContentChildren(BarComponent) accordionComponents: QueryList<BarComponent>;

 ngAfterViewInit() {
 this.accordionComponents.forEach((barComponent: BarComponent) => {
        barComponent.clicked.subscribe((bar: BarComponent) => doActionsOnBar(bar));           
    });
}
4
Mohsen Tabareh