Angular 11 中动态编译组件的推荐方法是什么?
What is the recommended way to dynamically compile component in Angular 11?
在我们的应用程序中,我们使用以下指令来显示动态 Angular 组件:
import {Compiler, Component, Directive, Input, ModuleWithComponentFactories, NgModule, OnDestroy, ViewContainerRef} from '@angular/core';
import {Render} from './render';
@Directive({
selector: '[appRender]',
exportAs: 'appRender'
})
export class RenderDirective implements OnDestroy {
@Input()
public set model(model: Render) {
this.compile(model);
}
constructor(private readonly viewContainerRef: ViewContainerRef,
private readonly compiler: Compiler) {
}
private compile(model: Render) {
@Component({template: model.template})
class TemplateComponent {
}
@NgModule({
imports: model.imports,
declarations: [TemplateComponent]
})
class TemplateModule {
}
this.compiler.compileModuleAndAllComponentsAsync(TemplateModule).then((factories: ModuleWithComponentFactories<any>) => {
const factory = factories.componentFactories.find(component => component.componentType === TemplateComponent);
const componentRef = this.viewContainerRef.createComponent(factory);
Object.assign(componentRef.instance, model.instance);
componentRef.hostView.detectChanges();
});
}
ngOnDestroy(): void {
this.viewContainerRef.clear();
}
}
我们正在 Angular 从 8.2 更新到 11。更新后我们面临以下错误:
ERROR Error: Angular JIT compilation failed: '@angular/compiler' not loaded!
- JIT compilation is discouraged for production use-cases! Consider AOT mode instead.
- Did you bootstrap using '@angular/platform-browser-dynamic' or '@angular/platform-server'?
- Alternatively provide the compiler with 'import "@angular/compiler";' before bootstrapping.
at getCompilerFacade (core.js:4086)
at Function.get (core.js:26924)
at getNgModuleDef (core.js:1139)
at new NgModuleFactory (core.js:25317)
at Compiler_compileModuleSync__POST_R3__ (core.js:28165)
at Compiler_compileModuleAndAllComponentsSync__POST_R3__ (core.js:28175)
at Compiler_compileModuleAndAllComponentsAsync__POST_R3__ [as compileModuleAndAllComponentsAsync] (core.js:28188)
at RenderDirective.compile (common.js:4560)
at RenderDirective.set model [as model] (common.js:4541)
at setInputsForProperty (core.js:10961)
我认为这与IVY编译器有关。问题是在 Angular 11 中获得相同结果的推荐方法是什么?
我通过以下方式解决了这个问题:
- 在 RenderModule
中提供 JitCompilerFactory
@NgModule({
declarations: [RenderDirective],
exports: [RenderDirective],
providers: [{provide: COMPILER_OPTIONS, useValue: {useJit: true}, multi: true},
{provide: CompilerFactory, useClass: JitCompilerFactory, deps: [COMPILER_OPTIONS]},
{provide: Compiler, useFactory: createCompiler, deps: [CompilerFactory]}]
})
export class RenderModule {
}
- 用函数定义替换带注释的组件和模块:
// BEFORE
private compile(model: Render) {
@Component({template: model.template})
class TemplateComponent {
}
@NgModule({
imports: model.imports,
declarations: [TemplateComponent]
})
class TemplateModule {
}
this.compiler.compileModuleAndAllComponentsAsync(TemplateModule).then((factories: ModuleWithComponentFactories<any>) => {
const factory = factories.componentFactories.find(component => component.componentType === TemplateComponent);
const componentRef = this.viewContainerRef.createComponent(factory);
Object.assign(componentRef.instance, model.instance);
componentRef.hostView.detectChanges();
});
}
// AFTER
private compile(model: Render) {
const templateComponent = Component({template: model.template})(class {});
const templateModule = NgModule({
imports: model.imports,
declarations: [templateComponent]
})(class {});
this.compiler.compileModuleAndAllComponentsAsync(templateModule).then((factories: ModuleWithComponentFactories<any>) => {
const factory = factories.componentFactories.find(component => component.componentType === templateComponent);
const componentRef = this.viewContainerRef.createComponent(factory);
Object.assign(componentRef.instance, model.instance);
componentRef.hostView.detectChanges();
}).catch(err => console.error(err));
}
这有效,但无法注入 TemplateComponent。
@Component({template: model.template})
class TemplateComponent {
constructor(private service: SomeService) { }
}
core.js:6210 ERROR Error: Uncaught (in promise): Error: This constructor is not compatible with Angular Dependency Injection because its dependency at index 0 of the parameter list is invalid.
This can happen if the dependency type is a primitive like a string or if an ancestor of this class is missing an Angular decorator.
在这里详细查看我的问题:
在我们的应用程序中,我们使用以下指令来显示动态 Angular 组件:
import {Compiler, Component, Directive, Input, ModuleWithComponentFactories, NgModule, OnDestroy, ViewContainerRef} from '@angular/core';
import {Render} from './render';
@Directive({
selector: '[appRender]',
exportAs: 'appRender'
})
export class RenderDirective implements OnDestroy {
@Input()
public set model(model: Render) {
this.compile(model);
}
constructor(private readonly viewContainerRef: ViewContainerRef,
private readonly compiler: Compiler) {
}
private compile(model: Render) {
@Component({template: model.template})
class TemplateComponent {
}
@NgModule({
imports: model.imports,
declarations: [TemplateComponent]
})
class TemplateModule {
}
this.compiler.compileModuleAndAllComponentsAsync(TemplateModule).then((factories: ModuleWithComponentFactories<any>) => {
const factory = factories.componentFactories.find(component => component.componentType === TemplateComponent);
const componentRef = this.viewContainerRef.createComponent(factory);
Object.assign(componentRef.instance, model.instance);
componentRef.hostView.detectChanges();
});
}
ngOnDestroy(): void {
this.viewContainerRef.clear();
}
}
我们正在 Angular 从 8.2 更新到 11。更新后我们面临以下错误:
ERROR Error: Angular JIT compilation failed: '@angular/compiler' not loaded!
- JIT compilation is discouraged for production use-cases! Consider AOT mode instead.
- Did you bootstrap using '@angular/platform-browser-dynamic' or '@angular/platform-server'?
- Alternatively provide the compiler with 'import "@angular/compiler";' before bootstrapping.
at getCompilerFacade (core.js:4086)
at Function.get (core.js:26924)
at getNgModuleDef (core.js:1139)
at new NgModuleFactory (core.js:25317)
at Compiler_compileModuleSync__POST_R3__ (core.js:28165)
at Compiler_compileModuleAndAllComponentsSync__POST_R3__ (core.js:28175)
at Compiler_compileModuleAndAllComponentsAsync__POST_R3__ [as compileModuleAndAllComponentsAsync] (core.js:28188)
at RenderDirective.compile (common.js:4560)
at RenderDirective.set model [as model] (common.js:4541)
at setInputsForProperty (core.js:10961)
我认为这与IVY编译器有关。问题是在 Angular 11 中获得相同结果的推荐方法是什么?
我通过以下方式解决了这个问题:
- 在 RenderModule 中提供
JitCompilerFactory
@NgModule({
declarations: [RenderDirective],
exports: [RenderDirective],
providers: [{provide: COMPILER_OPTIONS, useValue: {useJit: true}, multi: true},
{provide: CompilerFactory, useClass: JitCompilerFactory, deps: [COMPILER_OPTIONS]},
{provide: Compiler, useFactory: createCompiler, deps: [CompilerFactory]}]
})
export class RenderModule {
}
- 用函数定义替换带注释的组件和模块:
// BEFORE
private compile(model: Render) {
@Component({template: model.template})
class TemplateComponent {
}
@NgModule({
imports: model.imports,
declarations: [TemplateComponent]
})
class TemplateModule {
}
this.compiler.compileModuleAndAllComponentsAsync(TemplateModule).then((factories: ModuleWithComponentFactories<any>) => {
const factory = factories.componentFactories.find(component => component.componentType === TemplateComponent);
const componentRef = this.viewContainerRef.createComponent(factory);
Object.assign(componentRef.instance, model.instance);
componentRef.hostView.detectChanges();
});
}
// AFTER
private compile(model: Render) {
const templateComponent = Component({template: model.template})(class {});
const templateModule = NgModule({
imports: model.imports,
declarations: [templateComponent]
})(class {});
this.compiler.compileModuleAndAllComponentsAsync(templateModule).then((factories: ModuleWithComponentFactories<any>) => {
const factory = factories.componentFactories.find(component => component.componentType === templateComponent);
const componentRef = this.viewContainerRef.createComponent(factory);
Object.assign(componentRef.instance, model.instance);
componentRef.hostView.detectChanges();
}).catch(err => console.error(err));
}
这有效,但无法注入 TemplateComponent。
@Component({template: model.template})
class TemplateComponent {
constructor(private service: SomeService) { }
}
core.js:6210 ERROR Error: Uncaught (in promise): Error: This constructor is not compatible with Angular Dependency Injection because its dependency at index 0 of the parameter list is invalid. This can happen if the dependency type is a primitive like a string or if an ancestor of this class is missing an Angular decorator.
在这里详细查看我的问题: