使用动态模板创建动态组件(ComponentFactoryResolver)
Create dynamic component (ComponentFactoryResolver) with dynamic template
我想使用动态模板动态创建一个组件,以便此模板的插值将在动态组件的上下文中解析。
我知道我可以使用这段代码创建一个动态组件(必须在模块的entryComponents中提到):
我的静态组件:
@Component({
selector: 'html-type',
template: `<ng-template #htmlcontrolcomponent></ng-template>`
})
export class HtmlTypeComponent implements AfterViewInit{
@ViewChild('htmlcontrolcomponent', { read: ViewContainerRef }) entry: ViewContainerRef;
constructor(private resolver: ComponentFactoryResolver) {
super();
}
ngAfterViewInit() {
this.createComponent("<div>{{contextvar}}</div>");
}
createComponent(template) {
this.entry.clear();
const factory = this.resolver.resolveComponentFactory(HtmlControlComponent);
const componentRef = this.entry.createComponent(factory);
componentRef.instance.template = template; // this doesn't work, is there a way to do it?
}
需要动态添加的组件:
import { Component} from '@angular/core';
@Component({
selector: 'html-control',
template: '',
})
export class HtmlControlComponent {
contextvar: string = "This is my current context";
}
有没有办法重新分配动态创建的组件的模板?
我想实现的:动态组件的模板应该是动态的(由用户输入并经过清理)
您可以尝试将 <ng-content>
与 createComponent()
函数的 projectableNodes
参数一起使用。尝试以下
import { Component} from '@angular/core';
@Component({
selector: 'html-control',
template: `<ng-content></ng-content>`, // <-- add `<ng-content>` here
})
export class HtmlControlComponent {
contextvar: string = "This is my current context";
}
静态组件
createComponent(template) {
this.entry.clear();
const factory = this.resolver.resolveComponentFactory(HtmlControlComponent);
const componentRef = this.entry.createComponent(factory, 0, undefined, [[template]]); // <-- add `template` here
}
此处 [[template]]
参数被发送到类型 any[][]
的 projectableNodes
参数
未经测试的代码。可能需要解决一些问题。
我做到了...用不同的方法
我使用了 DynamicComponentService
重要:我必须在 angular.json 中关闭 "aot: false",否则会出现 Runtime compiler is not loaded
错误。
import {
Compiler,
Component,
ComponentFactory,
Injectable,
NgModule,
Type,
ViewContainerRef,
ViewEncapsulation
} from "@angular/core";
import {CommonModule} from "@angular/common";
@Injectable({
providedIn: "root"
})
export class DynamicComponentService {
protected cacheOfFactories: {[key: string]: ComponentFactory<any>};
protected componentCache: {[key: string]: Type<any>};
protected moduleCache: {[key: string]: Type<any>};
constructor(protected compiler: Compiler) {
this.cacheOfFactories = {};
this.componentCache = {};
this.moduleCache = {};
}
/**
*
* @param viewContainerRef
* @param selector
* @param template
*/
createComponentFactory(viewContainerRef: ViewContainerRef, selector: string, template: string) {
const componentFound = this.componentCache[selector];
if(componentFound) {
this.compiler.clearCacheFor(componentFound);
delete this.componentCache[selector];
}
const moduleFound = this.moduleCache[selector];
if(moduleFound) {
this.compiler.clearCacheFor(moduleFound);
delete this.moduleCache[selector];
}
viewContainerRef.clear();
this.componentCache[selector] = Component({
selector,
template,
encapsulation: ViewEncapsulation.None
})(class {
});
this.moduleCache[selector] = NgModule({
imports: [CommonModule],
declarations: [this.componentCache[selector]]
})(class {
});
return this.compiler.compileModuleAndAllComponentsAsync(this.moduleCache[selector])
.then((factories) => {
const foundFactory = factories.componentFactories.find((factory) => {
return factory.selector === selector;
});
if(foundFactory) {
return viewContainerRef.createComponent(foundFactory);
}
throw new Error("component not found");
})
.catch((error) => {
console.log("error", error);
this.compiler.clearCacheFor(componentFound);
delete this.componentCache[selector];
this.compiler.clearCacheFor(moduleFound);
delete this.moduleCache[selector];
return Promise.reject(error);
});
}
}
并将我的 html 类型组件更改为:
export class HtmlTypeComponent implements DoCheck {
@ViewChild('htmlcontrolcomponent', { read: ViewContainerRef }) entry: ViewContainerRef;
protected oldTemplate: string = "";
protected componentRef?: ComponentRef<any>;
constructor(private dynamicComponentService: DynamicComponentService) {}
ngDoCheck() {
if(this.entry && this.oldTemplate !== this.to.template) {
this.oldTemplate = this.to.template;
if(this.componentRef) {
this.componentRef.destroy();
delete this.componentRef;
}
this.entry.clear();
this.dynamicComponentService.createComponentFactory(this.entry, 'html-content', this.to.template)
.then((component) => {
this.componentRef = component;
this.componentRef.instance.model = this.model;
});
}
}
}
我什至可以去掉 HtmlControlComponent
我想使用动态模板动态创建一个组件,以便此模板的插值将在动态组件的上下文中解析。
我知道我可以使用这段代码创建一个动态组件(必须在模块的entryComponents中提到):
我的静态组件:
@Component({
selector: 'html-type',
template: `<ng-template #htmlcontrolcomponent></ng-template>`
})
export class HtmlTypeComponent implements AfterViewInit{
@ViewChild('htmlcontrolcomponent', { read: ViewContainerRef }) entry: ViewContainerRef;
constructor(private resolver: ComponentFactoryResolver) {
super();
}
ngAfterViewInit() {
this.createComponent("<div>{{contextvar}}</div>");
}
createComponent(template) {
this.entry.clear();
const factory = this.resolver.resolveComponentFactory(HtmlControlComponent);
const componentRef = this.entry.createComponent(factory);
componentRef.instance.template = template; // this doesn't work, is there a way to do it?
}
需要动态添加的组件:
import { Component} from '@angular/core';
@Component({
selector: 'html-control',
template: '',
})
export class HtmlControlComponent {
contextvar: string = "This is my current context";
}
有没有办法重新分配动态创建的组件的模板?
我想实现的:动态组件的模板应该是动态的(由用户输入并经过清理)
您可以尝试将 <ng-content>
与 createComponent()
函数的 projectableNodes
参数一起使用。尝试以下
import { Component} from '@angular/core';
@Component({
selector: 'html-control',
template: `<ng-content></ng-content>`, // <-- add `<ng-content>` here
})
export class HtmlControlComponent {
contextvar: string = "This is my current context";
}
静态组件
createComponent(template) {
this.entry.clear();
const factory = this.resolver.resolveComponentFactory(HtmlControlComponent);
const componentRef = this.entry.createComponent(factory, 0, undefined, [[template]]); // <-- add `template` here
}
此处 [[template]]
参数被发送到类型 any[][]
projectableNodes
参数
未经测试的代码。可能需要解决一些问题。
我做到了...用不同的方法
我使用了 DynamicComponentService
重要:我必须在 angular.json 中关闭 "aot: false",否则会出现 Runtime compiler is not loaded
错误。
import {
Compiler,
Component,
ComponentFactory,
Injectable,
NgModule,
Type,
ViewContainerRef,
ViewEncapsulation
} from "@angular/core";
import {CommonModule} from "@angular/common";
@Injectable({
providedIn: "root"
})
export class DynamicComponentService {
protected cacheOfFactories: {[key: string]: ComponentFactory<any>};
protected componentCache: {[key: string]: Type<any>};
protected moduleCache: {[key: string]: Type<any>};
constructor(protected compiler: Compiler) {
this.cacheOfFactories = {};
this.componentCache = {};
this.moduleCache = {};
}
/**
*
* @param viewContainerRef
* @param selector
* @param template
*/
createComponentFactory(viewContainerRef: ViewContainerRef, selector: string, template: string) {
const componentFound = this.componentCache[selector];
if(componentFound) {
this.compiler.clearCacheFor(componentFound);
delete this.componentCache[selector];
}
const moduleFound = this.moduleCache[selector];
if(moduleFound) {
this.compiler.clearCacheFor(moduleFound);
delete this.moduleCache[selector];
}
viewContainerRef.clear();
this.componentCache[selector] = Component({
selector,
template,
encapsulation: ViewEncapsulation.None
})(class {
});
this.moduleCache[selector] = NgModule({
imports: [CommonModule],
declarations: [this.componentCache[selector]]
})(class {
});
return this.compiler.compileModuleAndAllComponentsAsync(this.moduleCache[selector])
.then((factories) => {
const foundFactory = factories.componentFactories.find((factory) => {
return factory.selector === selector;
});
if(foundFactory) {
return viewContainerRef.createComponent(foundFactory);
}
throw new Error("component not found");
})
.catch((error) => {
console.log("error", error);
this.compiler.clearCacheFor(componentFound);
delete this.componentCache[selector];
this.compiler.clearCacheFor(moduleFound);
delete this.moduleCache[selector];
return Promise.reject(error);
});
}
}
并将我的 html 类型组件更改为:
export class HtmlTypeComponent implements DoCheck {
@ViewChild('htmlcontrolcomponent', { read: ViewContainerRef }) entry: ViewContainerRef;
protected oldTemplate: string = "";
protected componentRef?: ComponentRef<any>;
constructor(private dynamicComponentService: DynamicComponentService) {}
ngDoCheck() {
if(this.entry && this.oldTemplate !== this.to.template) {
this.oldTemplate = this.to.template;
if(this.componentRef) {
this.componentRef.destroy();
delete this.componentRef;
}
this.entry.clear();
this.dynamicComponentService.createComponentFactory(this.entry, 'html-content', this.to.template)
.then((component) => {
this.componentRef = component;
this.componentRef.instance.model = this.model;
});
}
}
}
我什至可以去掉 HtmlControlComponent