Angular2のDIで遊ぶ
Angular2のDIについて
ちまちま書いてたんですが度重なるR.C版リリースと
正式版リリースでかなりゴテゴテに回りました。
目指すところ
Angular2のDIをざっくりと理解する
Angular2のDIの書き方を理解する。
DI
一般的にDependency Injection(依存注入)と呼ぼれています。
ASP.netだとUnityで実現されているものですかね?
「 Providerから提供されているインスタンスを特定の変数にInject(注入)する仕組み 」
Angular2においては、上位(または同じ)Componentで
Provideされたインスタンスが注入されるような動作となっています。
//DIされる対象 @Injectable() export class HogeService{ ... } @Component({ selector: "hoge" template: "hogeComponent", provides: [HogeService] }) export class HogeComponent{ constructor( //インスタンスの注入 _hogeService: HogeService ){} ... }
上記のような使い方です。
これで何が嬉しいかというと、コンポーネント間でのデータのやり取りが
疎結合な状態で実現できるということですね。
例えば、上記コンストラクタで「_hogeService」が引数となっていますが
コンポーネント使用時は、これを意識せずに使用することが出来ます
(「出来る」であって意識しなくていいというわけではないです)。
コンポーネント間のDI
provideされたインスタンスは、下位階層のコンポーネントでも使用することが出来るようです。
その際、下位の階層では、provideプロパティを使用する必要はありません。
Angular2のDI構成は下図のようなイメージで可能かと思われます。
上記のCコンポーネントのような
Baseまでに別のコンポーネントを介する下位コンポーネントの場合でも
AコンポーネントでDIしていなくても
Cコンポーネントで上位コンポーネントでProvideされているインスタンスをDI可能なようです。
例ソース
例えば、下図の様な構成のコンポーネント郡を考えてみます。
BaseComponentにChildAComponent/ChildBComponentが存在します。
子コンポーネントはそれぞれ独立したServiceを持っています。
Component郡を包括するServiceはMainServiceのみです。
上記の構成を元に、AとBそれぞれのコンポーネントから
メインに対して文字列をPushする処理を作成してみます。
結果は、下図のようになります。
背景色の異なる領域が、それぞれ別のComponentです。
上図の動作から、別々のコンポーネントが
提供された同一のベースサービスを使用できていることがわかるかと思います。
上図の動作は、下記のようなソースの構成で動作しています。
BaseComponent
Component
@Component({ selector: "base-comp", template: `<h2>PushResult</h2> <div *ngFor="let item of _baseService.baseData"> pushComponent : {{item.pushComponent}} / pushText: {{item.pushText}} </div> <br> <br> <a-comp></a-comp> <b-comp></b-comp>`, providers: [BaseService] }) export class BaseComponent{ constructor( private _baseService: BaseService ){} }
Service
@Injectable() export class BaseService{ baseData: PushFileds[] = []; pushData(component: string, pushText: string){ var pushArray: PushFileds = {pushComponent: component, pushText: pushText}; this.baseData.push(pushArray); } getData(component: string) : PushFileds[]{ return this.baseData.filter((x:PushFileds) => x.pushComponent == component); } } class PushFileds{ pushComponent: string; pushText: string; }
Componentで、今回使用するモジュール全体で使用するBaseServiceをProvideしています。
Component上で使用するため、BaseComponentのConstructorでInjectしています。
BaseServiceは、子ServiceからPushされた値を、加工する機能と
加工した配列のフィルタを行い、結果配列を返却する機能を持っています。
ChildComponent
※A・B双方同様なソースのため、BComponentは割愛します。
Component
@Component({ selector: "a-comp", providers:[ChildAService], templateUrl: "app/acomp/childa.html" }) export class ChildAComponent{ acompText: string; constructor( private _childAService: ChildAService ){} pushText(){ this._childAService.pushText(this.acompText); } }
Service
@Injectable() export class ChildAService{ acompPushData = []; constructor( private _baseService: BaseService ){} pushText(pushText: string){ this._baseService.pushData("A", pushText); this.acompPushData = this._baseService.getData("A"); } }
html
<div style="background-color: azure;"> This is Acomponent <input [(ngModel)]="acompText" /> <button (click)="pushText()">Push</button> <br> りれき* <div *ngIf="_childAService.acompPushData"> <div *ngFor="let item of _childAService.acompPushData"> {{item.pushText}} </div> </div> </div>
ComponentでAComponent独自のAServiceをProvideしていますが
AserviceのConstructor上で
上位のBaseComponentでProvideされたBaseServiceを注入しています。
これを、AService/Bserviceの双方で行っているため
AとBで同一のインスタンスオブジェクトを使用している状態となります。
AComponentのPushとBComponentのPush内容が
同一のbaseServiceのbaseDataにPushされ描画されます。