Angular4 实战开发:(13) 动态模板与动态组件
appendChild()或insertBefore()方法来添加动态元素。但在Angular中,虽然我们也可以通过这两个方法来添加元素,但是如果元素的内容中包含事件绑定或动态属性时,你会发现它们都不生效,这是因为Angular并不会对你动态的内容进行解析渲染。那我们如何动态的插入模板和组件呢?insertBefore()的第二个参数就是嵌入点元素),而Angular的嵌入点一般是ViewContainerRef(表示视图容器,主要作用是创建和管理内嵌视图或组件视图。)。ViewcontainerRef的方式一般有两种:- 注入到构造函数并获取实例
- 通过
@ViewChild()来获取
/* src/app/components/trend-template.ts */
import {Component, OnInit, ViewContainerRef, Input, TemplateRef} from '@angular/core';
@Component({
selector: 'app-trend-template',
templateUrl: './trend-template.component.html',
styleUrls: ['./trend-template.component.scss']
})
export class TrendTemplateComponent implements OnInit {
@Input() template: any;
constructor(private _viewContainerRef: ViewContainerRef) { }
ngOnInit() {
if (this.template) {
this._viewContainerRef.createEmbeddedView(this.template);
}
}
}
在上面的代码中,我们将ViewContainerRef注入到构造函数,并将其实例赋给_viewContainerRef,同时给这个组件定义了一个输入属性template,用来接收模板,最后在OnInit生命周期中使用ViewContainerRef的createEmbeddedView()方法(它接收一个TemplateRef作为参数)将模板插入。// src/app/demo/demo-trend.component.html
<ng-template #tpl>
<button (click)="onclick()">点击试试</button>
</ng-template>
<app-trend-template [template]="tpl"></app-trend-template>

@ViewChild()方式:// src/app/demo/demo-trend.component.html
<ng-template #tpl2>
<button (click)="onclick()">点击试试</button>
</ng-template>
<div #viewcontainer></div>
还记得前面一章讲过的@ViewChild(),在这里我们用这个方法来获取模板和容器:
// src/app/demo/demo-trend.component.ts
@ViewChild('viewcontainer', {read: ViewContainerRef}) _viewContainerRef: ViewContainerRef;
@ViewChild('tpl2') template: TemplateRef<any>;
最后一样是使用createEmbeddedView()方法来插入模板:
ngOnInit() {
this._viewContainerRef.createEmbeddedView(this.template);
}

ComponentFactoryResolver(组件工厂解析器,类似Angular1中的$compile)。// src/app/components/trend-component.component.ts
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-trend-component',
templateUrl: './trend-component.component.html',
styleUrls: ['./trend-component.component.scss']
})
export class TrendComponentComponent implements OnInit {
title: string;
constructor() { }
ngOnInit() { }
}
// src/app/components/trend-component.component.html
<p>
{{title}}
</p>
下面以注入的形式来获取ComponentFactoryResolver实例:
// src/app/demo/demo-trend.component.ts
constructor(private _componentFactoryResolver: ComponentFactoryResolver) { }
ComponentFactoryResolver的resolveComponentFactory()方法(它接收一个组件类作为参数)来创建一个组件工厂:
// src/app/demo/demo-trend.component.ts
ngOnInit() {
const componentFactory = this._componentFactoryResolver.resolveComponentFactory(TrendComponentComponent);
}
ViewContainerRef,不过在这里使用的是它的另外一个方法:createComponent()来插入动态组件,它返回一个ComponentRef,我们可以通过ComponentRef的instance属性来获取组件实例,然后做一些赋值操作:// src/app/demo/demo-trend.component.ts
const componentRef = this._trendViewContainerRef.createComponent(componentFactory);
(<any>componentRef.instance).title = '我是动态组件';

entryComponents属性中添加要动态插入的组件类。
说明:entryComponents: 数组类型的选项,指定一系列的组件,这些组件将会在这个模块定义的时候进行编译Angular会为每一个组件创建一个ComponentFactory,然后把它存储在ComponentFactoryResolver。@NgModule({
declarations: [
...
],
imports: [
...
],
entryComponents: [TrendComponentComponent],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
