Angular 2.x+ 如何動態裝載組件

Angular 2.x+ 如何動態裝載組件

在 Angular 1.x 中咱們能夠使用 $compile 方法編譯模板達到動態加載組件的目的,然而在 ng2 中則沒有那麼簡單,下面的例子即爲動態加載廣告組件的過程。

live demo數組

首先,在添加組件前,咱們須要定義一個錨點用於標識組件插入的位置,廣告 banner 組件使用輔助指令 AdDirective 來標識模板中的有效插入位置。
// src/app/ad.directive.ts
import { Directive, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[ad-host]',
})

export class AdDirective {
  constructor(public viewContainerRef: ViewContainerRef) { }
}
AdDirective 經過注入 ViewContainerRef 來訪問做爲被插入組件宿主的節點視圖容器。
class ViewContainerRef { 
  createComponent(componentFactory: ComponentFactory<C>, index?: number, injector?: Injector, projectableNodes?: any[][]) : ComponentRef<C> { }
}
  1. Component 所對應的 ComponentFactory 便是編譯後的 Component 版本,全部與 Angular 運行時的實際交互都是經過 ComponentFactory 進行的
  2. 若是在 ViewContainerRef 中建立多個 Component/Template 那麼 index 表示當前動態組件插入的位置
  3. 默認 Injector 是繼承的,若是須要提供額外的 Provider,則須要使用 Injector 參數聲明注入器(IoC 容器)
  4. projectableNodes 參數表示組件的 Transclude

ng-template 就是應用 AdDirective 組件的地方,用來告訴 Angular 動態組件加載到哪裏。app

// src/app/ad-banner.component.ts (template)
template: `
            <div class="ad-banner">
              <h3>Advertisements</h3>
              <ng-template ad-host></ng-template>
            </div>
          `
ng-template 是建立動態組件較好的選擇,由於它不會渲染多餘的輸出。

AdBannerComponent 使用 AdItem 對象的數組做爲輸入屬性,並經過 getAds 方法週期性地遍歷 AdItem 對象數組,每隔 3s 調用 loadComponent 方法動態加載組件。ide

export class AdBannerComponent implements AfterViewInit, OnDestroy {
  @Input() ads: AdItem[];
  currentAddIndex: number = -1;
  @ViewChild(AdDirective) adHost: AdDirective;
  subscription: any;
  interval: any;

  constructor(private componentFactoryResolver: ComponentFactoryResolver) { }

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

  ngOnDestroy() {
    clearInterval(this.interval);
  }

  loadComponent() { ... }

  getAds() {
    this.interval = setInterval(() => {
      this.loadComponent();
    }, 3000);
  }
}
loadComponent 選擇某個廣告對象後,使用 ComponentFactoryResolver API 爲每一個相應的組件解析出一個 ComponentFactory 工廠類,用於建立組件的實例。
let adItem = this.ads[this.currentAddIndex];
let componentFactory = this.componentFactoryResolver.resolveComponentFactory(adItem.component);
這裏須要注意的是, ViewContainerRef 只能經過在構造函數中直接聲明來注入,由於 ViewContainerRef 的注入並不經過注入器,而是由編譯器直接注入的,因此沒法經過 Service 注入。
let viewContainerRef = this.adHost.viewContainerRef;
viewContainerRef.clear();
咱們能夠調用 viewContainerRefcreateComponent 方法實例化組件,該方法返回被加載的動態組件的引用,咱們能夠經過該引用修改組件的屬性或者調用其方法。
let componentRef = viewContainerRef.createComponent(componentFactory);
(<AdComponent>componentRef.instance).data = adItem.data;
同時這裏存在一個容易誤解的地方就是:建立動態組件必須經過 ViewContainerRef,實際上並非, ComponentFactory 自己就具備實例化組件的能力。
class ComponentFactory {
  create(injector: Injector, projectableNodes?: any[][], rootSelectorOrNode?: string|any) : ComponentRef<C> { }
}
相關文章
相關標籤/搜索