1. ホーム
  2. アンギュラー

[解決済み】Delegation: AngularのEventEmitterまたはObservable

2022-04-02 10:27:56

質問

Angularでデリゲーションパターンのようなものを実装しようとしています。 ユーザーが nav-item そして、その関数はイベントを発行し、イベントをリッスンしている他のコンポーネントによって処理されるようにしたいと思います。

以下はそのシナリオです。 Navigation コンポーネントを使用します。

import {Component, Output, EventEmitter} from 'angular2/core';

@Component({
    // other properties left out for brevity
    events : ['navchange'], 
    template:`
      <div class="nav-item" (click)="selectedNavItem(1)"></div>
    `
})

export class Navigation {

    @Output() navchange: EventEmitter<number> = new EventEmitter();

    selectedNavItem(item: number) {
        console.log('selected nav item ' + item);
        this.navchange.emit(item)
    }

}

以下は、観察するコンポーネントです。

export class ObservingComponent {

  // How do I observe the event ? 
  // <----------Observe/Register Event ?-------->

  public selectedNavItem(item: number) {
    console.log('item index changed!');
  }

}

重要なのは、観測コンポーネントに問題のイベントを観測させるにはどうすればいいかということです。

どのように解決するのか?

2016-06-27に更新しました。 を使用する代わりに、次のいずれかを使用します。

  • コメントで @Abdulrahman が推奨している BehaviorSubject、または
  • コメントで @Jason Goemaat が推奨する ReplaySubject。

A 件名 はObservableであり、(つまり subscribe() を呼び出すことができます)。 next() を実行し、新しい値を生成します。) この特徴を利用する。 Subjectは多くのObserversに値をマルチキャストすることができます。 私たちはこの機能を利用していません(Observerは1つだけです)。

ビヘイビアサブジェクト はSubjectの変種です。 これは、quot;現在の値"という概念を持っています。 ObservingComponentを作成すると、自動的にBehaviorSubjectから現在のナビゲーションアイテムの値が取得されます。

以下のコードと プランカー はBehaviorSubjectを使用しています。

リプレイサブジェクト はSubjectの別の変形です。実際に値が生成されるまで待ちたい場合は ReplaySubject(1) . BehaviorSubjectは初期値(すぐに提供される)を必要としますが、ReplaySubjectはそうではありません。 ReplaySubjectは常に最新の値を提供しますが、必須の初期値を持っていないため、サービスは最初の値を返す前に何らかの非同期処理を行うことができます。 しかし、初期値を必要としないため、サービスは最初の値を返す前に何らかの非同期処理を行うことができます。もし、1つの値だけが欲しいのであれば first() をサブスクリプションに追加します。 を使用する場合は、購読を中止する必要はありません。 first() .

import {Injectable}      from '@angular/core'
import {BehaviorSubject} from 'rxjs/BehaviorSubject';

@Injectable()
export class NavService {
  // Observable navItem source
  private _navItemSource = new BehaviorSubject<number>(0);
  // Observable navItem stream
  navItem$ = this._navItemSource.asObservable();
  // service command
  changeNav(number) {
    this._navItemSource.next(number);
  }
}

import {Component}    from '@angular/core';
import {NavService}   from './nav.service';
import {Subscription} from 'rxjs/Subscription';

@Component({
  selector: 'obs-comp',
  template: `obs component, item: {{item}}`
})
export class ObservingComponent {
  item: number;
  subscription:Subscription;
  constructor(private _navService:NavService) {}
  ngOnInit() {
    this.subscription = this._navService.navItem$
       .subscribe(item => this.item = item)
  }
  ngOnDestroy() {
    // prevent memory leak when component is destroyed
    this.subscription.unsubscribe();
  }
}

@Component({
  selector: 'my-nav',
  template:`
    <div class="nav-item" (click)="selectedNavItem(1)">nav 1 (click me)</div>
    <div class="nav-item" (click)="selectedNavItem(2)">nav 2 (click me)</div>`
})
export class Navigation {
  item = 1;
  constructor(private _navService:NavService) {}
  selectedNavItem(item: number) {
    console.log('selected nav item ' + item);
    this._navService.changeNav(item);
  }
}

プランカー


Observableを使用したオリジナルの回答です。 (BehaviorSubjectを使うよりも多くのコードとロジックが必要なので、お勧めしませんが、参考になるかもしれません)

では、Observableを使った実装を紹介します。 イベントエミッターの代わりに . 私の EventEmitter の実装とは異なり、この実装では、現在選択されている navItem このため、Observation コンポーネントが作成されたときに、APIコール navItem() で変更されたことが通知されます。 navChange$ Observableです。

import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/share';
import {Observer} from 'rxjs/Observer';

export class NavService {
  private _navItem = 0;
  navChange$: Observable<number>;
  private _observer: Observer;
  constructor() {
    this.navChange$ = new Observable(observer =>
      this._observer = observer).share();
    // share() allows multiple subscribers
  }
  changeNav(number) {
    this._navItem = number;
    this._observer.next(number);
  }
  navItem() {
    return this._navItem;
  }
}

@Component({
  selector: 'obs-comp',
  template: `obs component, item: {{item}}`
})
export class ObservingComponent {
  item: number;
  subscription: any;
  constructor(private _navService:NavService) {}
  ngOnInit() {
    this.item = this._navService.navItem();
    this.subscription = this._navService.navChange$.subscribe(
      item => this.selectedNavItem(item));
  }
  selectedNavItem(item: number) {
    this.item = item;
  }
  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

@Component({
  selector: 'my-nav',
  template:`
    <div class="nav-item" (click)="selectedNavItem(1)">nav 1 (click me)</div>
    <div class="nav-item" (click)="selectedNavItem(2)">nav 2 (click me)</div>
  `,
})
export class Navigation {
  item:number;
  constructor(private _navService:NavService) {}
  selectedNavItem(item: number) {
    console.log('selected nav item ' + item);
    this._navService.changeNav(item);
  }
}

プランカー


も参照してください。 Component Interaction Cookbook の例 を使用しています。 Subject をobservableに加えて使用します。 この例では、親と子のコミュニケーションですが、関連性のないコンポーネントにも同じ手法が適用できます。