イベントの発行とサブスクライブ (RxJS の利用)

ここでは、RxJS を用いたイベントの発行と購読の簡単な例を紹介します。

特にクロスコンポーネントでのイベントの発行と購読は、インジェクト可能なサービスとして実装して、 RxJS ライブラリの Subject と Subscription を用いて行います。オブザーバブルである Subject を 購読している側は、Subject 側で next を呼び出したタイミングでハンドラとして指定した関数が呼び出されます。

また、コンポーネント内でサブスクライブしたら、コンポーネントが破棄されるタイミングで、購読も終了 (unsubscribe) します。

イベントの発行と購読の例

ここでは「信号サービス」(シグナルサービス SignalService) というのを作ります。 信号サービスは 2 秒置きにイベントを自動的に発行します。それを購読する側のコンポーネントでは、 そのイベントを受けたときに、信号の色を変えて表示します。

「信号サービス」の作成

まずは Angular プロジェクトを作成します。プロジェクト名は自由ですが、ここでは "event1" とします。

$ ng new event1
$ cd event1

プロジェクト・ディレクトリに移動したら、続けてサービスを作成します。ここでは "service" というフォルダの下に "SignalService" という名前のサービスを作成します。

$ ng generate service service/signal --skipTests

さっそく service/signal.service.ts で SignalService を実装しましょう。 ここでは JavaScript の setInterval 関数を使って機械的に、2 秒置きにイベントを発行します。

イベントを発行するには RxJS で実装される Subject オブジェクトの next() メソッドを呼び出します。 ここでは next() に文字列のパラメータを渡せるように Subject<string>() として Subject を作成しています。

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

@Injectable({
  providedIn: 'root'
})
export class SignalService {

  signalChanged = new Subject<string>();
  current: string;

  constructor() {
    this.current = 'red';

    setInterval(() => {
      switch (this.current) {
        case 'red':
          this.current = 'green';
          break;
        case 'green':
          this.current = 'yellow';
          break;
        default:
          this.current = 'red';
          break;
      }

      this.signalChanged.next(this.current);
    }, 2000);
  }
}

このサービスを app.component.ts で利用します。

ディペンデンシ・インジェクションで取り込むので、app.module.ts で providers に SignalService を追加してください。長ったらしくなるのでコードは省略します。

コンポーネントの初期化と後始末はそれぞれ ngOnInitngOnDestroy でやりますので、 クラス定義のところで OnInitOnDestroy を implements に追加します。

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
import { SignalService } from './service/signal.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit, OnDestroy {
  signal = '';
  signalSubscription: Subscription;

  constructor(private signalService: SignalService) {}

  ngOnInit() {
    this.signalSubscription = this.signalService.signalChanged
      .subscribe((current: string) => {
      this.signal = current;
    });
  }

  ngOnDestroy() {
    this.signalSubscription.unsubscribe();
  }
}

さて、SignalService は signalChanged イベントを実装してますので、ngOnInit でそれを購読 (subscribe) します。 Subject はオブザーバブルなのでサブスクライブできます。

サブスクリライブした時に、その戻り値として RxJS の Subscription オブジェクトが返りますので、 これを保持しておき、コンポーネントの破棄のタイミング、すなわち ngOnDestroy にて、購読を終了 (unsubscribe) します。

この信号イベントを受け取る時に、信号の色をパラメータで受け取ります。これによって、テンプレートの CSS クラス名 (className) を切り替えています。

<div [className]="'circle ' + signal"></div>

CSS は次の通りです。

.circle {
    width:80px;
    height:80px;
    border-radius:40px;
}
.red {background-color: red;}
.green {background-color: green;}
.yellow {background-color: gold;}

以上で、イベントを発行するサービスを作成し、それをコンポーネントでサブスクライブ。 そして、コンポーネントを破棄するタイミングでアンサブスクライブ (購読終了) する例を示しました。