[Angular] ServiceからEventEmitterをつかってComponentにイベント通知する

``EventEmitter``は``Component``内でしか使えないという思い込みがありました。


## さんぷる

Google Charts API をロードするサービス。

[gmazzamuto/ng2-google-charts](https://github.com/gmazzamuto/ng2-google-charts)

を参考に作成しています。


```ts:google-charts-loader.service.ts
const LOAD_JS = "//www.gstatic.com/charts/loader.js" as string;
declare var google: any;

import { Injectable, EventEmitter } from "@angular/core";


@Injectable()
export class GoogleChartsLoaderService
{
  private scriptIsLoading: boolean = false;
  public scriptLoadingNotifier: EventEmitter<boolean> = new EventEmitter<boolean>();

  private _chartType: string = "ColumnChart";
  private chartPackages: {[id: string]: string} = {
    ColumnChart : "corechart",
  };


  set chartType(chartType: string)
  {
    this._chartType = chartType;
  }


  public load(): Promise<any>
  {
    return new Promise((resolve: any = Function.prototype, reject: any = Function.prototype) => {
      this.loadScript()
        .then(() => {
          google.charts.load("current", {
            packages : [this.chartPackages[this._chartType]],
            callback : resolve,
          })
        })
        .catch(err => console.error("Google charts script could not be loaded."));
    });
  }


  private loadScript(): Promise<any>
  {
    return new Promise((resolve: any = Function.prototype, reject: any = Function.prototype) => {
      if (typeof google !== "undefined") {
        resolve();
      } else if (! this.scriptIsLoading) {
        let script    = document.createElement("script");
        script.src    = LOAD_JS;
        script.async  = true;
        script.defer  = true;
        script.onload = () => {
          this.scriptIsLoading = true;
          this.scriptLoadingNotifier.emit(this.scriptIsLoading);
          resolve();
        };
        script.onerror  = () => {
          this.scriptIsLoading = false;
          this.scriptLoadingNotifier.emit(this.scriptIsLoading);
          reject();
        };
        document.body.appendChild(script);
      }
    });
  }

  public check(): void
  {
    this.scriptLoadingNotifier.emit(this.scriptIsLoading);
  }
}
```

### イベントのうけとり

[EventEmitter.subscribe()](https://angular.io/api/core/EventEmitter) を利用して Componentでイベントをうけとる。


```ts:sample.component.ts
import { Component, OnInit } from "@angular/core";
import { GoogleChartsLoaderService } from "./google-charts-loader.service";

@Component({
  selector : "my-app",
  template : "<h1>Hello Angular</h1>",
})
export class MyAppComponent implements OnInit
{
  public constructor(
    private googleChartsLoaderService: GoogleChartsLoaderService
  ) {
    console.log("constructor()");
    this.googleChartsLoaderService
      .scriptLoadingNotifier
      .subscribe((data: boolean) => console.log("subscribe()", data));
  }

  /**
   * Angular LifeCycle
   */
  public ngOnInit()
  {
    console.log("ngOnInit()");
    this.googleChartsLoaderService.load();
    this.googleChartsLoaderService.check();
  }
}
```

### コンソール
```shell-session
constructor()
ngOnInit()
subscribe() false
Angular is running in the development mode. Call enableProdMode() to enable the production mode.
subscribe() true
```


### 動作

``load()``が先に呼ばれるけど、非同期処理なので``check()``がすぐに呼ばれる。
この時点で**this.scriptIsLoading === false** なので、``console.log``は``false``
その後、``load()``が完了して、再びイベントが発行され、``true``となる 。



これで、いままでできていなかったことが実現できそう。