首先说明主旨意思,动态 template 模板,表示 @Component(template:动态改变这里的内容)。假设一个场景,如我未来计划开发的一个前端页面生成工具,通过json schema或者drag and drop方式动态来生成form,并且支持代码下载和复制,粘贴即可使用。
我们知道angular提供了ComponentFactoryResolver抽象类,使用方法componentFactoryResolver.resolveComponentFactory()可以动态的创建Component

  1. export abstract class ComponentFactoryResolver {
  2. static NULL: ComponentFactoryResolver = new _NullComponentFactoryResolver();
  3. abstract resolveComponentFactory<T>(component: Type<T>): ComponentFactory<T>;
  4. }

从源码可以看出,方法接收参数为component: Type<T>。所以我们只需要传一个Component类就可以动态创建组件了。这满足我们很多的使用场景,但是,对于开头我说的,我需要动态的template模板,或者假设这里有一个textarea输入框,往里边粘贴一段正确的Angular html模板代码,然后可以自动动态编译模板展示对应效果。这样也是可以做到的,需要通过 Compiler 类来实现

class Compiler {
 compileModuleSync<T>(moduleType: Type<T>): NgModuleFactory<T>
 compileModuleAsync<T>(moduleType: Type<T>): Promise<NgModuleFactory<T>>
 compileModuleAndAllComponentsSync<T>(moduleType: Type<T>): ModuleWithComponentFactories<T>
 compileModuleAndAllComponentsAsync<T>(moduleType: Type<T>): Promise<ModuleWithComponentFactories<T>>
 clearCache(): void
 clearCacheFor(type: Type<any>)
}

从源码可以看出,Compiler 类中提供带有ompileModule-xxx前缀的方法是可以实现动态编译NgModule的,而第二、三个方法,可以实现编译Component。 下边我就举例使用 compileModuleAndAllComponentsSync 方法实现动态template模板动态创建Component demo

代码演示

import {
 Compiler, Component, NgModule, OnInit, ViewChild,
 ViewContainerRef
} from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
@Component({
 selector: 'my-app',
 template: `<h1>Dynamic template:</h1>
            <div #container></div>
             <button  (click)="dynamicTest()">click me to dymanic compile component</button>
            `
})
export class AppComponent implements OnInit {
 @ViewChild('container', { read: ViewContainerRef }) container: ViewContainerRef;
 constructor(private compiler: Compiler) { }
 ngOnInit() {
 }
 dynamicTest() {
   this.addComponent(
     `<h4  (click)="increaseCounter()">
        Click to increase: {{counter}}
      </h4>
      `,
     {
       counter: 1,
       increaseCounter: function () {
         this.counter++;
       }
     }
   );
 }
 private addComponent(template: string, properties: any = {}) {
   @Component({ template })
   class TemplateComponent { }
   @NgModule({ declarations: [TemplateComponent] })
   class TemplateModule { }
   const mod = this.compiler.compileModuleAndAllComponentsSync(TemplateModule);
   const factory = mod.componentFactories.find((comp) =>
     comp.componentType === TemplateComponent
   );
   const component = this.container.createComponent(factory);
   Object.assign(component.instance, properties);
   // If properties are changed at a later stage, the change detection
   // may need to be triggered manually:
   // component.changeDetectorRef.detectChanges();
 }
}

上边代码效果在线demo演示:dynamic-template-to-compile-dynamic-component-DEMO

总结

Angular 提供 ViewContainerRefTemplateRefComponentFactoryResolver 使得我们定制渲染和动态创建组件轻松实现。然后 Compiler类的提供,使得满足一些特殊的场景。顿时感觉动态组件功能又强大的很多,官网文档对这些介绍都不多,只能自己找资料或者看源码摸索。
文章提到的 前端页面生成工具 工程为 ng-form-builder,写于本篇文章分享当天创建的工程,掐指一算,开发周期应该比较长,目前只有个人开发,也是工作不忙或者业余时间持续开发,总之会坚持到底,该工具会节约页面开发时间,去掉重复性的copy/paste工作,提供效率。