首先说明主旨意思,动态 template 模板,表示 @Component(template:动态改变这里的内容)。假设一个场景,如我未来计划开发的一个前端页面生成工具,通过json schema或者drag and drop方式动态来生成form,并且支持代码下载和复制,粘贴即可使用。
我们知道angular提供了ComponentFactoryResolver抽象类,使用方法componentFactoryResolver.resolveComponentFactory()可以动态的创建Component
export abstract class ComponentFactoryResolver {static NULL: ComponentFactoryResolver = new _NullComponentFactoryResolver();abstract resolveComponentFactory<T>(component: Type<T>): ComponentFactory<T>;}
从源码可以看出,方法接收参数为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 提供 ViewContainerRef、TemplateRef 和 ComponentFactoryResolver 使得我们定制渲染和动态创建组件轻松实现。然后 Compiler类的提供,使得满足一些特殊的场景。顿时感觉动态组件功能又强大的很多,官网文档对这些介绍都不多,只能自己找资料或者看源码摸索。
文章提到的 前端页面生成工具 工程为 ng-form-builder,写于本篇文章分享当天创建的工程,掐指一算,开发周期应该比较长,目前只有个人开发,也是工作不忙或者业余时间持续开发,总之会坚持到底,该工具会节约页面开发时间,去掉重复性的copy/paste工作,提供效率。
