Posted on Leave a comment

[功能介紹-5] 動態載入元件

模版組件有的時後會需要能夠動態被載入,在這邊會講要如何利用ComponentFactoryResolver來動態加入組件

本篇的例子是需要動態載入廣告橫幅,因為廣告內容會由幾個不同的團隊來打造,要在同一個區塊循環播放不同的廣告,因此較難把不同的廣告放在同一個component,這時後就會需要用到動態載入的功能
完整範例請見: live example / download example

The anchor directive

在架構介紹的地方有介紹到,directive是一個沒有view的component,可以使用在html裡。
在這邊因為需要動態載入元件,會需要先建立一個directive,讓angular知道要把要動態載入的元件放在那邊。
ad.directive.ts這個檔案的內容如下:

import { Directive, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[ad-host]',//這個directive的名字
})
export class AdDirective {
  constructor(public viewContainerRef: ViewContainerRef) { }//在directive裡去取得View元件以方便做操作
}

首先我們將這個directive命名為[ad-host],然後注入ViewContainerRef,ViewContainerRef可以讓我們得知目前所在的HTML元素中包含的View內容,也可以透過它來改變View的結果(ex: 動態的產生Component、移除某個Component等等)。

Loading components

使用上面的directive的html碼如下

<div class="ad-banner">
   <h3>Advertisements</h3>
   <ng-template ad-host></ng-template>
</div>

這邊可以看到我們使用ng-template來應用剛剛所創建的directive,ng-template指令表示一個Angular模板,這個標籤的內容將包含template的一部分,然後可以與其他template一起組成來成為最終的組件模板。使用ng-template標籤只是定義一個template,但是我們還沒有使用它。

Resolving components

下面是src/app/ad-banner.component.ts的內容

export class AdBannerComponent implements AfterViewInit, OnDestroy {
  @Input() ads: AdItem[];//要輪播顯示的廣告
  currentAddIndex: number = -1;
  @ViewChild(AdDirective) adHost: AdDirective;//宣告剛剛建立的Directive,並藉由取得傳入的adHost
  subscription: any;
  interval: any;

  constructor(private componentFactoryResolver: ComponentFactoryResolver) { }

  ngAfterViewInit() {
    this.loadComponent();
    this.getAds();
  }

  ngOnDestroy() {
    clearInterval(this.interval);
  }
  //最重要動態載入廣告的程式碼都在這個function裡
  loadComponent() {
    //決定要顯示那則廣告
    this.currentAddIndex = (this.currentAddIndex + 1) % this.ads.length;
    let adItem = this.ads[this.currentAddIndex];

    //透過ComponentFactoryResolver來解析component組件
    let componentFactory = this.componentFactoryResolver.resolveComponentFactory(adItem.component);
    //透過剛剛從AdDirective傳來的資訊取得要載入ad的template
    let viewContainerRef = this.adHost.viewContainerRef;
    //將template內容清掉
    viewContainerRef.clear();
    //動態建立template並顯示在畫面上
    let componentRef = viewContainerRef.createComponent(componentFactory);
    (componentRef.instance).data = adItem.data;
  }

  //通過這個方法,每三秒會動態載入不同的廣告元件
  getAds() {
    this.interval = setInterval(() => {
      this.loadComponent();
    }, 3000);
  }
}

loadComponent是動態載入元件最關鍵的程式碼片段,程式碼裡都有下註解。

最終出來的成果如下圖

參考資料

Leave a Reply