全国のFlexファンの皆様、お久しぶりです!
ゴーリスト開発の盛次(モリツグ)です。
前回のAngular2とFlexを比較してみたの影響で全国に90人はいると思われるFlex愛好者たちがAngular2に目覚めてくれたことだと思います。
今日は以下のような方に向けた内容になります。
・Angular2で動的にコンポーネントを追加したい方
・Angular2でもaddChildしてselectedIndexしたくて我慢できない方
ViewStackのデモを触ってみる
Flex経験者でない方にViewStackの説明が必要ですね!
ViewStackとはコンポーネントを格納するためのコンテナです、複数コンポーネントを格納できますが、表示するのは現在選択中のものだけです。
Angular2では標準で画面遷移の仕組みが用意されており、遷移時にServiceを差し込めたりと非常に便利です。
しかし、動的にaddChildしてselectedIndex=0したい人は少なくないと思います。
むしろFlex愛好家は「なんでViewStackないん?」と思っているはずです。
とりあえず完成品をご覧ください。ViewStackデモ
リンク先のplunkerでは以下のような画面が表示されます。
動作は以下のようになっています。
・ViewStackには初期化時に3つのコンポーネントを格納
・左側のボタン3つに対応したコンポーネントを追加
・selectで選んだコンポーネントだけを表示
・removeボタンを押すと選択中のコンポーネントを削除
コンポーネントを動的に追加する方法
- 動的に追加したいコンポーネントを@NgModuleのdeclarationsだけでなくentryComponentsにも追記する。
- ComponentFactoryResolverに追加したいコンポーネントを渡して目的のクラスのComponentFactoryを作成する。
- ComponentFactoryをViewContainerRef.createComponentの引数にする。
src/app.ts
@NgModule({ declarations: [.....], imports: [....], bootstrap: [App ], // 動的に追加するコンポーネントはここに指定 entryComponents: [ChildA, ChildB, ChildC] }) export class AppModule {}
src/app/viewstack/ViewStack.ts
@Component({ selector: 'view-stack', providers: [], template: ` <div #container> </div> ` }) export class ViewStack implements AfterViewInit { @ViewChild('container', {read: ViewContainerRef}) private container:ViewContainerRef; public constructor( private _componentFactoryResolver: ComponentFactoryResolver //, private _viewContainerRef: ViewContainerRef ) {} public addChild<T>(childClass: Type<T>, index:number=-1):void { const componentFactory:ComponentFactory<T> = this._componentFactoryResolver.resolveComponentFactory(childClass); const componentRef:ComponentRef<T> = this.container.createComponent(componentFactory); } }
ViewContainerRef.createComponentがどの要素に対して作成したコンポーネントを追加するのかが重要です。
このソースを見る限りthis.container.createComponentしているために以下のようなhtmlになりそうです。
<app> ... <view-stack> <div> <child-a>... <child-b>... <child-c>... </div> </view-stack> ... </app>
しかし、実際は以下のようになります。
<app> ... <view-stack> <div> </div> <child-a>... <child-b>... <child-c>... </view-stack> ... </app>
ViewContainerRef.createComponentは親の要素に作成したコンポーネントを追加するようです。
ダミー的に<div #container>を利用しているのはこのためです。
仮にconstructorでinjectしたViewContainerRefを利用した場合、以下のようなhtmlになります。
<app> ... <view-stack> <div> </div> </view-stack> <child-a>... <child-b>... <child-c>... ... </app>
まとめ
addChildしてselectedIndexしたかったのでAngular2でViewStackを作ってみました。
*ngForと[ngSwitch]を利用すれば別の実装手段もありますが、一番疎結合なのはentryComponentsを使う今回の方法だと思います。
Flex経験者を取り込んでAngular2の人口が増えることを願って書いています。