動(dòng)態(tài)創(chuàng)建組件

潮州網(wǎng)站建設(shè)公司成都創(chuàng)新互聯(lián)公司,潮州網(wǎng)站設(shè)計(jì)制作,有大型網(wǎng)站制作公司豐富經(jīng)驗(yàn)。已為潮州近千家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\外貿(mào)網(wǎng)站制作要多少錢(qián),請(qǐng)找那個(gè)售后服務(wù)好的潮州做網(wǎng)站的公司定做!
這篇文章我們將介紹在 Angular 中如何動(dòng)態(tài)創(chuàng)建組件。
定義 AlertComponent 組件
首先,我們需要定義一個(gè)組件。
exe-alert.component.ts
import { Component, Input } from '@angular/core';
@Component({
selector: "exe-alert",
template: `
<h2>Alert {{type}}</h2>
`,
})
export class AlertComponent {
@Input() type: string = "success";
}
上面代碼中,我們定義了一個(gè)簡(jiǎn)單的 alert 組件,該組件有一個(gè)輸入屬性 type ,用于讓用戶自定義提示的類(lèi)型。我們的自定義組件最終是一個(gè)實(shí)際的 DOM 元素,因此如果我們需要在頁(yè)面中插入該元素,我們就需要考慮在哪里放置該元素。
創(chuàng)建組件容器
在 Angular 中放置組件的地方稱(chēng)為 container 容器。接下來(lái),我們將在 exe-app 組件中創(chuàng)建一個(gè)模板元素,此外我們使用模板變量的語(yǔ)法,聲明一個(gè)模板變量。接下來(lái)模板元素 <ng-template> 將會(huì)作為我們的組件容器,具體示例如下:
app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'exe-app',
template: `
<ng-template #alertContainer></ng-template>
`
})
export class AppComponent { }
友情提示:容器可以是任意的 DOM 元素或組件。
在 AppComponent 組件中,我們可以通過(guò) ViewChild 裝飾器來(lái)獲取視圖中的模板元素,如果沒(méi)有指定第二個(gè)查詢(xún)參數(shù),則默認(rèn)返回的組件實(shí)例或相應(yīng)的 DOM 元素,但這個(gè)示例中,我們需要獲取 ViewContainerRef 實(shí)例。
ViewContainerRef 用于表示一個(gè)視圖容器,可添加一個(gè)或多個(gè)視圖。通過(guò) ViewContainerRef 實(shí)例,我們可以基于 TemplateRef 實(shí)例創(chuàng)建內(nèi)嵌視圖,并能指定內(nèi)嵌視圖的插入位置,也可以方便對(duì)視圖容器中已有的視圖進(jìn)行管理。簡(jiǎn)而言之,ViewContainerRef 的主要作用是創(chuàng)建和管理內(nèi)嵌視圖或組件視圖。
根據(jù)以上需求,更新后的代碼如下:
import { Component, ViewChild, ViewContainerRef } from '@angular/core';
@Component({
selector: 'exe-app',
template: `
<ng-template #alertContainer></ng-template>
`
})
export class AppComponent {
@ViewChild("alertContainer", { read: ViewContainerRef }) container: ViewContainerRef;
}
動(dòng)態(tài)創(chuàng)建組件
接下來(lái),在 AppComponent 組件中,我們來(lái)添加兩個(gè)按鈕,用于創(chuàng)建 AlertComponent 組件。
import { Component, ViewChild, ViewContainerRef } from '@angular/core';
@Component({
selector: 'exe-app',
template: `
<ng-template #alertContainer></ng-template>
<button (click)="createComponent('success')">Create success alert</button>
<button (click)="createComponent('danger')">Create danger alert</button>
`
})
export class AppComponent {
@ViewChild("alertContainer", { read: ViewContainerRef }) container: ViewContainerRef;
}
在我們定義 createComponent() 方法前,我們需要注入 ComponentFactoryResolver 服務(wù)對(duì)象。該 ComponentFactoryResolver 服務(wù)對(duì)象中,提供了一個(gè)很重要的方法 - resolveComponentFactory() ,該方法接收一個(gè)組件類(lèi)作為參數(shù),并返回 ComponentFactory 。
ComponentFactoryResolver 抽象類(lèi):
export abstract class ComponentFactoryResolver {
static NULL: ComponentFactoryResolver = new _NullComponentFactoryResolver();
abstract resolveComponentFactory<T>(component: Type<T>): ComponentFactory<T>;
}在 AppComponent 組件構(gòu)造函數(shù)中,注入 ComponentFactoryResolver 服務(wù):
constructor(private resolver: ComponentFactoryResolver) {}接下來(lái)我們?cè)賮?lái)看一下 ComponentFactory 抽象類(lèi):
export abstract class ComponentFactory<C> {
abstract get selector(): string;
abstract get componentType(): Type<any>;
// selector for all <ng-content> elements in the component.
abstract get ngContentSelectors(): string[];
// the inputs of the component.
abstract get inputs(): {propName: string, templateName: string}[];
// the outputs of the component.
abstract get outputs(): {propName: string, templateName: string}[];
// Creates a new component.
abstract create(
injector: Injector, projectableNodes?: any[][], rootSelectorOrNode?: string|any,
ngModule?: NgModuleRef<any>): ComponentRef<C>;
}
通過(guò)觀察 ComponentFactory 抽象類(lèi),我們知道可以通過(guò)調(diào)用 ComponentFactory 實(shí)例的 create() 方法,來(lái)創(chuàng)建組件。介紹完上面的知識(shí),我們來(lái)實(shí)現(xiàn) AppComponent 組件的 createComponent() 方法:
createComponent(type) {
this.container.clear();
const factory: ComponentFactory =
this.resolver.resolveComponentFactory(AlertComponent);
this.componentRef: ComponentRef = this.container.createComponent(factory);
}接下來(lái)我們來(lái)分段解釋一下上面的代碼。
this.container.clear();
每次我們需要?jiǎng)?chuàng)建組件時(shí),我們需要?jiǎng)h除之前的視圖,否則組件容器中會(huì)出現(xiàn)多個(gè)視圖 (如果允許多個(gè)組件的話,就不需要執(zhí)行清除操作 )。
正如我們之前所說(shuō)的,resolveComponentFactory() 方法接受一個(gè)組件并返回如何創(chuàng)建組件的 ComponentFactory 實(shí)例。
在上面代碼中,我們調(diào)用容器的 createComponent() 方法,該方法內(nèi)部將調(diào)用 ComponentFactory 實(shí)例的 create() 方法創(chuàng)建對(duì)應(yīng)的組件,并將組件添加到我們的容器。
現(xiàn)在我們已經(jīng)能獲取新組件的引用,即可以我們可以設(shè)置組件的輸入類(lèi)型:
this.componentRef.instance.type = type;
同樣我們也可以訂閱組件的輸出屬性,具體如下:
this.componentRef.instance.output.subscribe(event => console.log(event));
另外不能忘記銷(xiāo)毀組件:
ngOnDestroy() {
this.componentRef.destroy();
}最后我們需要將動(dòng)態(tài)組件添加到 NgModule 的 entryComponents 屬性中:
@NgModule({
...,
declarations: [AppComponent, AlertComponent],
bootstrap: [AppComponent],
entryComponents: [AlertComponent],
})
export class AppModule { }完整示例
exe-alert.component.ts
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: "exe-alert",
template: `
<h2 (click)="output.next(type)">Alert {{type}}</h2>
`,
})
export class AlertComponent {
@Input() type: string = "success";
@Output() output = new EventEmitter();
}
app.component.ts
import {
Component, ViewChild, ViewContainerRef, ComponentFactory,
ComponentRef, ComponentFactoryResolver, OnDestroy
} from '@angular/core';
import { AlertComponent } from './exe-alert.component';
@Component({
selector: 'exe-app',
template: `
<ng-template #alertContainer></ng-template>
<button (click)="createComponent('success')">Create success alert</button>
<button (click)="createComponent('danger')">Create danger alert</button>
`
})
export class AppComponent implements OnDestroy {
componentRef: ComponentRef<AlertComponent>;
@ViewChild("alertContainer", { read: ViewContainerRef }) container: ViewContainerRef;
constructor(private resolver: ComponentFactoryResolver) { }
createComponent(type: string) {
this.container.clear();
const factory: ComponentFactory<AlertComponent> =
this.resolver.resolveComponentFactory(AlertComponent);
this.componentRef = this.container.createComponent(factory);
this.componentRef.instance.type = type;
this.componentRef.instance.output.subscribe((msg: string) => console.log(msg));
}
ngOnDestroy() {
this.componentRef.destroy()
}
}
app.module.ts
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { AlertComponent } from './exe-alert.component';
@NgModule({
imports: [BrowserModule],
declarations: [AppComponent, AlertComponent],
bootstrap: [AppComponent],
entryComponents: [AlertComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppModule { }
總結(jié)
獲取裝載動(dòng)態(tài)組件的容器
在組件類(lèi)的構(gòu)造函數(shù)中,注入 ComponentFactoryResolver 對(duì)象
調(diào)用 ComponentFactoryResolver 對(duì)象的 resolveComponentFactory() 方法創(chuàng)建 ComponentFactory 對(duì)象
調(diào)用組件容器對(duì)象的 createComponent() 方法創(chuàng)建組件并自動(dòng)添加動(dòng)態(tài)組件到組件容器中
基于返回的 ComponentRef 組件實(shí)例,配置組件相關(guān)屬性 (可選)
在模塊 Metadata 對(duì)象的 entryComponents 屬性中添加動(dòng)態(tài)組件
declarations - 用于指定屬于該模塊的指令和管道列表
entryComponents - 用于指定在模塊定義時(shí),需要編譯的組件列表。對(duì)于列表中聲明的每個(gè)組件,Angular 將會(huì)創(chuàng)建對(duì)應(yīng)的一個(gè) ComponentFactory 對(duì)象,并將其存儲(chǔ)在 ComponentFactoryResolver 對(duì)象中
我有話說(shuō)
<ng-template> 與 <ng-container> 有什么區(qū)別?
通常情況下,當(dāng)我們使用結(jié)構(gòu)指令時(shí),我們需要添加額外的標(biāo)簽來(lái)封裝內(nèi)容,如使用 *ngIf 指令:
<section *ngIf="show"> <div> <h3>Div one</h3> </div> <div> <h3>Div two</h3> </div> </section>
上面示例中,我們?cè)?section 標(biāo)簽上應(yīng)用了 ngIf 指令,從而實(shí)現(xiàn) section 標(biāo)簽內(nèi)容的動(dòng)態(tài)顯示。這種方式有個(gè)問(wèn)題是,我們必須添加額外的 DOM 元素。要解決該問(wèn)題,我們可以使用 <ng-template> 的標(biāo)準(zhǔn)語(yǔ)法 (非*ngIf語(yǔ)法糖):
<ng-template [ngIf]="show"> <div> <h3>Div one</h3> </div> <div> <h3>Div two</h3> </div> </ng-template>
問(wèn)題是解決了但我們不再使用 * 語(yǔ)法糖語(yǔ)法,這樣會(huì)導(dǎo)致我們代碼的不統(tǒng)一。雖然解決了問(wèn)題,但又帶來(lái)了新問(wèn)題。那我們還有其它的方案么?答案是有的,我們可以使用 ng-container 指令。
<ng-container>
<ng-container> 是一個(gè)邏輯容器,可用于對(duì)節(jié)點(diǎn)進(jìn)行分組,但不作為 DOM 樹(shù)中的節(jié)點(diǎn),它將被渲染為 HTML中的 comment 元素。使用 <ng-container> 的示例如下:
<ng-container *ngIf="show"> <div> <h3>Div one</h3> </div> <div> <h3>Div two</h3> </div> </ng-container>
有時(shí)我們需要根據(jù) switch 語(yǔ)句,動(dòng)態(tài)顯示文本,這時(shí)我們需要添加一個(gè)額外的標(biāo)簽如 <span> ,具體示例如下:
<div [ngSwitch]="value"> <span *ngSwitchCase="0">Text one</span> <span *ngSwitchCase="1">Text two</span> </div>
針對(duì)這種情況,理論上我們是不需要添加額外的 <span> 標(biāo)簽,這時(shí)我們可以使用 ng-container 來(lái)解決這個(gè)問(wèn)題:
<div [ngSwitch]="value"> <ng-container *ngSwitchCase="0">Text one</ng-container> <ng-container *ngSwitchCase="1">Text two</ng-container> </div>
介紹完 ng-container 指令,我們來(lái)分析一下它跟 ng-template 指令有什么區(qū)別?我們先看以下示例:
<ng-template> <p> In template, no attributes. </p> </ng-template> <ng-container> <p> In ng-container, no attributes. </p> </ng-container>
以上代碼運(yùn)行后,瀏覽器中輸出結(jié)果是:
In ng-container, no attributes.
即 <ng-template> 中的內(nèi)容不會(huì)顯示。當(dāng)在上面的模板中添加 ngIf 指令:
<template [ngIf]="true"> <p> ngIf with a template.</p> </template> <ng-container *ngIf="true"> <p> ngIf with an ng-container.</p> </ng-container>
以上代碼運(yùn)行后,瀏覽器中輸出結(jié)果是:
ngIf with a template.
ngIf with an ng-container.
現(xiàn)在我們來(lái)總結(jié)一下 <ng-template> 和 <ng-container> 的區(qū)別:
最后再來(lái)看一個(gè) <ng-container> 的使用示例:
模板定義
<div> <ng-container *ngIf="true"> <h3>Title</h3> <div>Content</div> </ng-container> </div>
渲染結(jié)果
<div>
<!--bindings={
"ng-reflect-ng-if": "true"
}--><!---->
<h3>Title</h3>
<div>Content</div>
</div>
TemplateRef 與 ViewContainerRef 有什么作用?
TemplateRef
用于表示內(nèi)嵌的 template 模板元素,通過(guò) TemplateRef 實(shí)例,我們可以方便創(chuàng)建內(nèi)嵌視圖(Embedded Views),且可以輕松地訪問(wèn)到通過(guò) ElementRef 封裝后的 nativeElement。需要注意的是組件視圖中的 template 模板元素,經(jīng)過(guò)渲染后會(huì)被替換成 comment 元素。
ViewContainerRef
用于表示一個(gè)視圖容器,可添加一個(gè)或多個(gè)視圖。通 ViewContainerRef 實(shí)例,我們可以基于 TemplateRef 實(shí)例創(chuàng)建內(nèi)嵌視圖,并能指定內(nèi)嵌視圖的插入位置,也可以方便對(duì)視圖容器中已有的視圖進(jìn)行管理。簡(jiǎn)而言之,ViewContainerRef 的主要作用是創(chuàng)建和管理內(nèi)嵌視圖或組件視圖。(本示例就是通過(guò) ViewContainerRef 對(duì)象提供的 API來(lái)動(dòng)態(tài)地創(chuàng)建組件視圖)。
ViewChild 裝飾器還支持哪些查詢(xún)條件?
ViewChild 裝飾器用于獲取模板視圖中的元素,它支持 Type 類(lèi)型或 string 類(lèi)型的選擇器,同時(shí)支持設(shè)置 read 查詢(xún)條件,以獲取不同類(lèi)型的實(shí)例。
export interface ViewChildDecorator {
// Type類(lèi)型:@ViewChild(ChildComponent)
// string類(lèi)型:@ViewChild('tpl', { read: ViewContainerRef })
(selector: Type<any>|Function|string, {read}?: {read?: any}): any;
new (selector: Type<any>|Function|string,
{read}?: {read?: any}): ViewChild;
}
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。
當(dāng)前名稱(chēng):詳解Angular4.x動(dòng)態(tài)創(chuàng)建組件
文章鏈接:http://chinadenli.net/article6/ppceog.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制網(wǎng)站、全網(wǎng)營(yíng)銷(xiāo)推廣、品牌網(wǎng)站建設(shè)、標(biāo)簽優(yōu)化、商城網(wǎng)站、外貿(mào)網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)