angular

Angular で Service のシングルトン化と非シングルトン化

はじめに

  • Angular で Service を扱う際、チュートリアルの通りにすると Service がシングルトンとなり、アプリケーション内のどこから参照しても同一のインスタンスが参照される。
  • 呼び出し元の文脈によって Service に別の状態をもたせたいと思った場合、非シングルトンとして扱いたくなったのでやり方を調べてまとめてみた。

Service のシングルトン化

  • まずはチュートリアルに記載されている通りにシングルトンとして Service を作成し設定し使用してみる。

Service の作成

ng generate service hero --module=app

Service が DI 可能になるように設定

  • アプリケーション内のどこでも DI で取得できるように AppModule@NgModule.providers に登録する。
@NgModule({
  providers: [
    HeroService, // 作成した HeroService の追加
    MessageService
  ],
})
export class AppModule {

Service の使用

  • 任意の Component や Service の constructor() の引数として対象の Service を指定することで DI により Service を取得することができる。
@Component()
export class BarComponent {
  construtor(
    public heroService: HeroService // この heroService は AppModule でキャッシュされ、アプリケーション内のどこでも同一のインスタンスが参照される
  ) { }
}

Service の非シングルトン化

  • Service の作成と仕様の箇所はシングルトンの場合と同様。
  • AppModule に登録する際に下記のようにすると毎回インスタンスが生成されて非シングルトンとして扱うことができる。
@NgModule({
  providers: [
    // HeroService の DI 時の動作について定義する
    { 
      // DI で提供するクラス名を指定
      provide: HeroService, 
      // 初期化処理を指定
      useFactory: () => { return new HeroService(); }, 
      // 初期化処理で DI するクラスを指定
      deps: []
    },
    MessageService
  ],
})
export class AppModule {
  • HeroService の constructor に引数がある場合は useFactory を constructor に合わせて修正し、deps オプションに依存するクラスを指定する。
@NgModule({
  providers: [
    // HeroService の DI 時の動作について定義する
    { 
      // DI で提供するクラス名を指定
      provide: HeroService, 
      // 初期化処理を指定
      // HeroService では constructor(arg1: Arg1Class, arg2: Arg2Class) という定義になっているものとする。
      useFactory: (arg1: Arg1Class, arg2: Arg2Class) => { return new HeroService(arg1, arg2); }, 
      // 初期化処理で DI するクラスを指定
      deps: [Arg1Class, Arg2Class]
    },
    MessageService
  ],
})
export class AppModule {

Service の非シングルトン化その2 〜 キャッシュするスコープをコントロールする

  • チュートリアルでは AppModule@NgModule.providers に Service の DI 設定をすることでアプリケーション全体で同一インスタンスを参照するように設定したが、任意の Component の @Component.providers に同様の設定をすることで、その Component 以下で同一のインスタンスを参照するようになる。

<component-1>
  <component-2></component-2>
  <component-3></component-3>
</component-1>
<component-a>
  <component-b></component-b>
  <component-c></component-c>
</component-a>
  • component-* がすべて独自に定義した Component のセレクターであり、セレクター同士で親子関係を持つものとする。
  • component-1@Component.providers に HeroService を設定すると component-1, component-2, component-3 で同一の HeroService インスタンスを参照するようになる。
  • component-1component-a でそれぞれ @Component.providers に HeroService を設定したとすると、component-1, component-2, component-3 で同一の HeroService インスタンスを参照し、component-a, component-b, component-c で前出のものとは別でグループ内では同一の HeroService インスタンスを参照する。
  • component-* すべての @Component.providers に HeroService を設定したとすると、それぞれ別の HeroService インスタンスを参照することになり、実質的に非シングルトンとして参照することができる。

参考資料