TL;DR
テンプレート以外からイベントの購読処理を行うケースでは,rxjs
を使いましょう.
EventEmitterを使うのは危険です
EventEmitterについて
EventEmitter
は,コンポーネントからその上位コンポーネントへの,任意の値の通知を提供します.
つまり,
@Output()
someAction = new EventEmitter<SomeObject>();
func() {
this.someAction.emit({ foo: 'bar' })
}
のようにしておけば,その上位コンポーネントで
<app-child (someAction)="onSomeAction($event)"></app-child>
のように値を受け取れる,というわけです.
サービスクラスでの通知処理
では,コンポーネントではなく,サービスクラス等から,何らかの値を通知したい場合を考えます.
先程との違いは,実際の購読処理を行う責務が誰にあるかです.
先程での例では,開発者はテンプレート内で通知の処理方法を記述し,実際の購読処理を行う責務はAngularにありました.
今回の場合,実際の購読処理を行う責務は開発者にあります.
これを踏まえて,先程のEventEmitter
を使うと,以下のように書けそうです.
以下は間違った例です
class TestService {
someChange = new EventEmitter<SomeObject>();
}
testService.someChange.subscribe(obj => ...)
確かにEventEmitter
はSubject<T>
を継承していますが,このように書くのは間違いです.
EventEmitter
の購読方法はあくまで内部的なもので,これからも同じ方法で購読できる保証はありません.
https://stackoverflow.com/a/36076701/3094842
つまり,EventEmitter
の購読処理を行う責務を,開発者が負うことはできない,ということです.
これについて,このようなケースでは,単にrxjs
を使うと良いようです.
先程のコードをrxjs
を使用して書き直すと,以下のようになります.
class TestService {
private internalSomeObject: SomeObject;
someChange$ = new BehaviorSubject<SomeObject>(undefined);
get someObject() { return this.internalSomeObject; }
set someObject(someObject: SomeObject) {
this.someChange$.next(someObject);
this.internalSomeObject = someObject;
}
}
これで,コンポーネントから認証情報の変更を検知できるようになります.
testService.someChange$.subscribe((someObject: SomeObject) => {
// 変更を検知したときの処理
});