概要
過去の記事で
を書いた時に、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を意識しなくても扱えるようになっていますが、複雑なことをするときに必要になる知識だと思うのでその時に関係性を把握していると理解の助けになると思います。