Angular 4.x 动态创建组件
动态创建组件这篇文章我们将介绍在 Angular 中如何动态创建组件。 定义 AlertComponent 组件首先,我们需要定义一个组件。 exe-alert.component.ts import { Component,Input } from '@angular/core'; @Component({ selector: "exe-alert",template: ` <h1>Alert {{type}}</h1> `,}) export class AlertComponent { @Input() type: string = "success"; } 上面代码中,我们定义了一个简单的 创建组件容器在 Angular 中放置组件的地方称为 app.component.ts import { Component } from '@angular/core'; @Component({ selector: 'exe-app',template: ` <ng-template #alertContainer></ng-template> ` }) export class AppComponent { }
在 AppComponent 组件中,我们可以通过
根据以上需求,更新后的代码如下: 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; } 动态创建组件接下来,在 AppComponent 组件中,我们来添加两个按钮,用于创建 AlertComponent 组件。 import { Component,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; } 在我们定义 ComponentFactoryResolver 抽象类: export abstract class ComponentFactoryResolver { static NULL: ComponentFactoryResolver = new _NullComponentFactoryResolver(); abstract resolveComponentFactory<T>(component: Type<T>): ComponentFactory<T>; } 在 AppComponent 组件构造函数中,注入 constructor(private resolver: ComponentFactoryResolver) {} 接下来我们再来看一下 ComponentFactory 抽象类: 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>; } 通过观察 ComponentFactory 抽象类,我们知道可以通过调用 ComponentFactory 实例的 createComponent(type) { this.container.clear(); const factory: ComponentFactory = this.resolver.resolveComponentFactory(AlertComponent); this.componentRef: ComponentRef = this.container.createComponent(factory); } 接下来我们来分段解释一下上面的代码。 this.container.clear(); 每次我们需要创建组件时,我们需要删除之前的视图,否则组件容器中会出现多个视图 (如果允许多个组件的话,就不需要执行清除操作 )。 const factory: ComponentFactory = this.resolver.resolveComponentFactory(AlertComponent); 正如我们之前所说的, this.componentRef: ComponentRef = this.container.createComponent(factory); 在上面代码中,我们调用容器的 现在我们已经能获取新组件的引用,即可以我们可以设置组件的输入类型: this.componentRef.instance.type = type; 同样我们也可以订阅组件的输出属性,具体如下: this.componentRef.instance.output.subscribe(event => console.log(event)); 另外不能忘记销毁组件: ngOnDestroy() { this.componentRef.destroy(); } 最后我们需要将动态组件添加到 NgModule 的 entryComponents 属性中: @NgModule({ ...,declarations: [AppComponent,AlertComponent],bootstrap: [AppComponent],entryComponents: [AlertComponent],}) export class AppModule { } 完整示例exe-alert.component.tsimport { Component,Input,Output,EventEmitter } from '@angular/core'; @Component({ selector: "exe-alert",template: ` <h1 (click)="output.next(type)">Alert {{type}}</h1> `,}) export class AlertComponent { @Input() type: string = "success"; @Output() output = new EventEmitter(); } app.component.tsimport { Component,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.tsimport { 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],schemas: [CUSTOM_ELEMENTS_SCHEMA] }) export class AppModule { } 线上示例 - Plunker 总结动态加载组件的流程:
我有话说
|