W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
&本節(jié)講的是一個用于顯示廣告的范例,而部分廣告攔截器插件,比如 Chrome 的 AdGuard,可能會破壞其工作邏輯,因此,請在本頁關閉那些插件。
組件的模板不會永遠是固定的。應用可能會需要在運行期間加載一些新的組件。
下面的例子展示了如何構建動態(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ù)組作為輸入,它最終來自 AdService
。 AdItem
對象指定要加載的組件類,以及綁定到該組件上的任意數(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)用 ViewContainerRef
的 createComponent()
。
createComponent()
方法返回一個引用,指向這個剛剛加載的組件。 使用這個引用就可以與該組件進行交互,比如設置它的屬性或調(diào)用它的方法。
通常,Angular 編譯器會為模板中所引用的每個組件都生成一個 ComponentFactory
類。 但是,對于動態(tài)加載的組件,模板中不會出現(xiàn)對它們的選擇器的引用。
要想確保編譯器照常生成工廠類,就要把這些動態(tài)加載的組件添加到 NgModule
的 entryComponents
數(shù)組中:
Path:"src/app/app.module.ts (entry components)" 。
entryComponents: [ HeroJobAdComponent, HeroProfileComponent ],
在廣告條中,所有組件都實現(xiàn)了一個公共接口 AdComponent
,它定義了一個標準化的 API,來把數(shù)據(jù)傳給組件。
下面就是兩個范例組件及其 AdComponent
接口:
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;
}
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;
}
export interface AdComponent {
data: any;
}
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: