Angular9動態(tài)組件

2020-07-01 14:47 更新

&本節(jié)講的是一個用于顯示廣告的范例,而部分廣告攔截器插件,比如 Chrome 的 AdGuard,可能會破壞其工作邏輯,因此,請在本頁關閉那些插件。

組件的模板不會永遠是固定的。應用可能會需要在運行期間加載一些新的組件。

動態(tài)組件加載

下面的例子展示了如何構建動態(tài)廣告條。

英雄管理局正在計劃一個廣告活動,要在廣告條中顯示一系列不同的廣告。幾個不同的小組可能會頻繁加入新的廣告組件。 再用只支持靜態(tài)組件結構的模板顯然是不現(xiàn)實的。

你需要一種新的組件加載方式,它不需要在廣告條組件的模板中引用固定的組件。

Angular 自帶的 API 就能支持動態(tài)加載組件。

指令

在添加組件之前,先要定義一個錨點來告訴 Angular 要把組件插入到什么地方。

廣告條使用一個名叫 AdDirective 的輔助指令來在模板中標記出有效的插入點。

Path:"src/app/ad.directive.ts" 。

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


@Directive({
  selector: '[ad-host]',
})
export class AdDirective {
  constructor(public viewContainerRef: ViewContainerRef) { }
}

AdDirective 注入了 ViewContainerRef 來獲取對容器視圖的訪問權,這個容器就是那些動態(tài)加入的組件的宿主。

@Directive 裝飾器中,要注意選擇器的名稱:ad-host,它就是你將應用到元素上的指令。

加載組件

廣告條的大部分實現(xiàn)代碼都在 "ad-banner.component.ts" 中。 為了讓這個例子簡單點,HTML 被直接放在了 @Component 裝飾器的 template 屬性中。

<ng-template> 元素就是剛才制作的指令將應用到的地方。 要應用 AdDirective,回憶一下來自 "ad.directive.ts" 的選擇器 ad-host。把它應用到 <ng-template>(不用帶方括號)。 這下,Angular 就知道該把組件動態(tài)加載到哪里了。

Path:"src/app/ad-banner.component.ts (template)" 。

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

<ng-template> 元素是動態(tài)加載組件的最佳選擇,因為它不會渲染任何額外的輸出。

解析組件

深入看看 "ad-banner.component.ts" 中的方法。

AdBannerComponent 接收一個 AdItem 對象的數(shù)組作為輸入,它最終來自 AdServiceAdItem 對象指定要加載的組件類,以及綁定到該組件上的任意數(shù)據(jù)。 AdService 可以返回廣告活動中的那些廣告。

AdBannerComponent 傳入一個組件數(shù)組可以在模板中放入一個廣告的動態(tài)列表,而不用寫死在模板中。

通過 getAds() 方法,AdBannerComponent 可以循環(huán)遍歷 AdItems 的數(shù)組,并且每三秒調(diào)用一次 loadComponent() 來加載新組件。

Path:"src/app/ad-banner.component.ts (excerpt)" 。

export class AdBannerComponent implements OnInit, OnDestroy {
  @Input() ads: AdItem[];
  currentAdIndex = -1;
  @ViewChild(AdDirective, {static: true}) adHost: AdDirective;
  interval: any;


  constructor(private componentFactoryResolver: ComponentFactoryResolver) { }


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


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


  loadComponent() {
    this.currentAdIndex = (this.currentAdIndex + 1) % this.ads.length;
    const adItem = this.ads[this.currentAdIndex];


    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(adItem.component);


    const viewContainerRef = this.adHost.viewContainerRef;
    viewContainerRef.clear();


    const componentRef = viewContainerRef.createComponent(componentFactory);
    (<AdComponent>componentRef.instance).data = adItem.data;
  }


  getAds() {
    this.interval = setInterval(() => {
      this.loadComponent();
    }, 3000);
  }
}

這里的 loadComponent() 方法很重要。 來一步步看看。首先,它選取了一個廣告。

&loadComponent() 如何選擇廣告

&loadComponent() 方法使用某種算法選擇了一個廣告。

&(譯注:循環(huán)選取算法)首先,它把 currentAdIndex 遞增一,然后用它除以 AdItem 數(shù)組長度的余數(shù)作為新的 currentAdIndex 的值, 最后用這個值來從數(shù)組中選取一個 adItem。

loadComponent() 選取了一個廣告之后,它使用 ComponentFactoryResolver 來為每個具體的組件解析出一個 ComponentFactory。 然后 ComponentFactory 會為每一個組件創(chuàng)建一個實例。

接下來,你要把 viewContainerRef 指向這個組件的現(xiàn)有實例。但你怎么才能找到這個實例呢? 很簡單,因為它指向了 adHost,而這個 adHost 就是你以前設置過的指令,用來告訴 Angular 該把動態(tài)組件插入到什么位置。

回憶一下,AdDirective 曾在它的構造函數(shù)中注入了一個 ViewContainerRef。 因此這個指令可以訪問到這個你打算用作動態(tài)組件宿主的元素。

要把這個組件添加到模板中,你可以調(diào)用 ViewContainerRefcreateComponent()

createComponent() 方法返回一個引用,指向這個剛剛加載的組件。 使用這個引用就可以與該組件進行交互,比如設置它的屬性或調(diào)用它的方法。

對選擇器的引用

通常,Angular 編譯器會為模板中所引用的每個組件都生成一個 ComponentFactory 類。 但是,對于動態(tài)加載的組件,模板中不會出現(xiàn)對它們的選擇器的引用。

要想確保編譯器照常生成工廠類,就要把這些動態(tài)加載的組件添加到 NgModuleentryComponents 數(shù)組中:

Path:"src/app/app.module.ts (entry components)" 。

entryComponents: [ HeroJobAdComponent, HeroProfileComponent ],

公共的 AdComponent 接口

在廣告條中,所有組件都實現(xiàn)了一個公共接口 AdComponent,它定義了一個標準化的 API,來把數(shù)據(jù)傳給組件。

下面就是兩個范例組件及其 AdComponent 接口:

  1. Path:"src/app/hero-job-ad.component.ts" 。

    import { Component, Input } from '@angular/core';


    import { AdComponent }      from './ad.component';


    @Component({
      template: `
        <div class="job-ad">
          <h4>{{data.headline}}</h4>


          {{data.body}}
        </div>
      `
    })
    export class HeroJobAdComponent implements AdComponent {
      @Input() data: any;


    }

  1. Path:"hero-profile.component.ts" 。

    import { Component, Input }  from '@angular/core';


    import { AdComponent }       from './ad.component';


    @Component({
      template: `
        <div class="hero-profile">
          <h3>Featured Hero Profile</h3>
          <h4>{{data.name}}</h4>


          <p>{{data.bio}}</p>


          <strong>Hire this hero today!</strong>
        </div>
      `
    })
    export class HeroProfileComponent implements AdComponent {
      @Input() data: any;
    }

  1. Path:"ad.component.ts" 。

    export interface AdComponent {
      data: any;
    }

結果展示

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號