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 { }