概要
過去の記事で
を書いた時に、AngularのInjector
とProvider
とDependency
の関係を理解していないとよく分からないと思ったので追記的にまとめます。
環境
- Angular 4.3.5
覚え方
結論から言うと、以下のように考えるとすっきりします。
項目 | 役割 |
---|---|
Injector | 料理人 |
Provider | レシピ |
Dependency | 料理 |
説明
通常のDIの流れ
ref: Dependency Injection in Angular by thoughtram
InjectorがProviderを元にDependencyを生成するAngularのDIの流れです。
これを先程の考え方にすると料理人がレシピを見て料理を作るという感じになります。
コードで表すと以下のような関係です。
import { ReflectiveInjector } from '@angular/core'; // injector var injector = ReflectiveInjector.resolveAndCreate([ // providers { provide: Car, useClass: Car }, { provide: Engine, useClass: Engine }, { provide: Tires, useClass: Tires }, { provide: Doors, useClass: Doors } ]); var car = injector.get(Car); // dependency
通常のAngularのDIではシンタックスシュガーによってよりシンプルな書き方で扱えるようになってます。
階層的DI
ref: Dependency Injection in Angular by thoughtram
Angularは親のInjector(料理人A)
から子のInjector(料理人B)
を生成することができ、そのInjectorは親で用意したProvider(レシピ)
からDependency(料理)
を生成できます。上の画像では子がCar
を生成していますね。
親から子がレシピを教えてもらう、みたいな感じです。
当然Injector(料理人)
が違えば作るDependency(料理)
も違うものになるので、コード上でも以下のようになります。
var injector = ReflectiveInjector.resolveAndCreate([Engine]); var childInjector = injector.resolveAndCreateChild([Engine]); injector.get(Engine) !== childInjector.get(Engine);
Modalの件だとどこに当てはまるか
Angularで中身を動的に変えられるModalを作る【応用編】 - Carpe Diem
では
open(data: any, provider: Provider): void { if (!data) { return; } const providers = ReflectiveInjector.resolve([provider]); const injector = ReflectiveInjector.fromResolvedProviders(providers, this.vcr.parentInjector); const factory = this.resolver.resolveComponentFactory(data); const component = this.vcr.createComponent(factory, this.vcr.length, injector);
としていますが、このinjector
を
const injector = ReflectiveInjector.resolveAndCreate(providers);
とはできません。独自のinjector使おうとすると
No provider for ViewUtils!
が出ます。これはCreateComponent
する時にComponentのdependency以外に
createComponent
は内部でComponentFactory
のcreate
というメソッドを使用しているのですが、
create( injector: Injector, projectableNodes: any[][] = null, rootSelectorOrNode: string|any = null): ComponentRef<C> { const vu: ViewUtils = injector.get(ViewUtils); if (!projectableNodes) { projectableNodes = []; } const hostView: AppView<any> = new this._viewClass(vu, null, null, null); return hostView.createHostView(rootSelectorOrNode, injector, projectableNodes); }
ref: angular/component_factory.ts at ab26b6518d16e54d198031c9994399cc29e270f1 · angular/angular · GitHub
injector.get(ViewUtils);
とあるようにViewUtils
のProvider(レシピ)
が必要です。
これを作ってくれるInjector(料理人)
が必要だけど、独自だとそのProvider(レシピ)
を持ってないから作れないのです。
なのでvcr
のparentInjector
から引っ張ってくる必要があります。
まとめ
基本的にAngularはInjectorを意識しなくても扱えるようになっていますが、複雑なことをするときに必要になる知識だと思うのでその時に関係性を把握していると理解の助けになると思います。