Angular 動態(tài)組件

2022-06-29 11:21 更新

動態(tài)組件加載器

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

組件的模板不會永遠是固定的。應用可能會需要在運行期間加載一些新的組件。這本烹飪書為你展示如何使用 ?ComponentFactoryResolver ?來動態(tài)添加組件。

現(xiàn)場演練 / 下載范例查看本節(jié)的源碼。

動態(tài)組件加載

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

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

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

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

指令

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

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

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

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

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

在 ?@Directive? 裝飾器中,要注意選擇器的名稱:?ad-host?,它就是你將應用到元素上的指令。下一節(jié)會展示該如何做。

加載組件

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

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

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

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

解析組件

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

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

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

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

export class AdBannerComponent implements OnInit, OnDestroy {
  @Input() ads: AdItem[] = [];

  currentAdIndex = -1;

  @ViewChild(AdDirective, {static: true}) adHost!: AdDirective;
  interval: number|undefined;

  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 viewContainerRef = this.adHost.viewContainerRef;
    viewContainerRef.clear();

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

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

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

?loadComponent()? 如何選擇廣告
?loadComponent()? 方法使用某種算法選擇了一個廣告。
(譯注:循環(huán)選取算法)首先,它把 ?currentAdIndex ?遞增一,然后用它除以 ?AdItem ?數(shù)組長度的余數(shù)作為新的 ?currentAdIndex ?的值, 最后用這個值來從數(shù)組中選取一個 ?adItem?。

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

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

要把這個組件添加到模板中,你可以調(diào)用 ?ViewContainerRef ?的 ?createComponent()?。

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

公共的 AdComponent 接口

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

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

  • 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;
    }
  • 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;
    }
  • ad.component.ts
  • export interface AdComponent {
      data: any;
    }

最終的廣告欄

最終的廣告欄是這樣的:



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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號