首页 专题 H5案例 前端导航 UI框架

Angular4 实战开发:(13) 动态模板与动态组件

作者:TG 日期: 2017-07-11 字数: 8512 阅读: 11910
在原生Javascript中,我们可以使用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生命周期中使用ViewContainerRefcreateEmbeddedView()方法(它接收一个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()方式:

我们再来添加一个模板元素和一个div:

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


接着使用ComponentFactoryResolverresolveComponentFactory()方法(它接收一个组件类作为参数)来创建一个组件工厂:

// src/app/demo/demo-trend.component.ts   

ngOnInit() {   

  const componentFactory = this._componentFactoryResolver.resolveComponentFactory(TrendComponentComponent);   

}

然后还是要依靠ViewContainerRef,不过在这里使用的是它的另外一个方法:createComponent()来插入动态组件,它返回一个ComponentRef,我们可以通过ComponentRefinstance属性来获取组件实例,然后做一些赋值操作:

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

如有疑问或建议,欢迎在下方的评论区留言!

目录