はまったりひらめいたり…とか…

Angularや.NETやAzureやその他色々。

Angular2のDIで遊ぶ

Angular2のDIについて

ちまちま書いてたんですが度重なるR.C版リリースと

正式版リリースでかなりゴテゴテに回りました。

目指すところ

  • Angular2のDIをざっくりと理解する

  • Angular2のDIの書き方を理解する。

DI

一般的にDependency Injection(依存注入)と呼ぼれています。

ASP.netだとUnityで実現されているものですかね?

「 Providerから提供されているインスタンスを特定の変数にInject(注入)する仕組み 」

qiita.com

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構成は下図のようなイメージで可能かと思われます。

f:id:TakasDev:20161005134255p:plain

上記のCコンポーネントのような

Baseまでに別のコンポーネントを介する下位コンポーネントの場合でも

AコンポーネントでDIしていなくても

Cコンポーネントで上位コンポーネントでProvideされているインスタンスをDI可能なようです。

例ソース

例えば、下図の様な構成のコンポーネント郡を考えてみます。

f:id:TakasDev:20161005090256p:plain

BaseComponentにChildAComponent/ChildBComponentが存在します。

コンポーネントはそれぞれ独立したServiceを持っています。

Component郡を包括するServiceはMainServiceのみです。

上記の構成を元に、AとBそれぞれのコンポーネントから

メインに対して文字列をPushする処理を作成してみます。

結果は、下図のようになります。

背景色の異なる領域が、それぞれ別のComponentです。

f:id:TakasDev:20161005092157g:plain

上図の動作から、別々のコンポーネント

提供された同一のベースサービスを使用できていることがわかるかと思います。

上図の動作は、下記のようなソースの構成で動作しています。

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され描画されます。