免责声明:这个问题的灵感来自于 Medium 上的“ Here is what you need to know about dynamic components in Angular ”。
有一位作者展示了如何动态生成模块和组件,将后者放入前者,动态编译模块,获取组件工厂并使用它通过ViewContainerRef生成组件。
这在基本设置中工作得很好。
现在让我们假设我们想要动态生成一个组件,但这次使用的是使用异步管道的模板。
接下来,我们需要让这个东西在生产环境中工作,这意味着两件事:
- AOT已启用
- enableProdMode()已在引导应用程序时调用
由于 Angular 编译器不可用,我们可以为应用程序根模块中的 Compiler 令牌提供一个jit 编译器的实例。
app.module.ts:
import { BrowserModule } from '@angular/platform-browser';
import {Compiler, NgModule} from '@angular/core';
import {AppComponent } from './app.component';
import {JitCompilerFactory} from '@angular/platform-browser-dynamic';
export function jitCompiler() {
/*
* Cast JitCompilerFactory to any because
* jit compiler factory constructor does not
* accept any arguments according to the @angular/platform-browser-dynamic/src/compiler_factory.d.ts
*/
const klass = (JitCompilerFactory as any);
return new klass([]).createCompiler([
{
useJit: true
}
]);
}
@NgModule({
declarations: [
AppComponent
],
providers: [
{
provide: Compiler,
useFactory: jitCompiler
}
],
imports: [
BrowserModule
],
bootstrap: [AppComponent]
})
export class AppModule { }
app.component.ts
import {
AfterViewInit,
ChangeDetectionStrategy,
Compiler,
Component,
NgModule,
ViewContainerRef
} from '@angular/core';
import {Subject} from 'rxjs';
import {CommonModule} from '@angular/common';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppComponent implements AfterViewInit {
constructor(
private readonly compiler: Compiler,
private readonly vcr: ViewContainerRef
) {}
ngAfterViewInit() {
const template = '<span>Hello, {{text$ }}</span>';
const cmp = Component({
template
})(class DynamicComponent {
text$: Subject<string>;
});
const module = NgModule({
declarations: [cmp],
imports: [CommonModule],
providers: []
})(class DynamicModule {});
this.compiler.compileModuleAndAllComponentsAsync(module)
.then(compiledModule => {
const dynamicComponent = this.vcr.createComponent(compiledModule.componentFactories[0]);
const sub = new Subject();
sub.next('World!');
dynamicComponent.instance.text$ = sub;
// Just to simulate some server updates
setTimeout(() => dynamicComponent.instance.text$.next('Another'), 3e3);
});
}
}
如果我们运行它并打开 DevTools,我们可以看到以下错误:
ERROR 错误:模块“function(){}”导入了意外的值“function(){}”。请添加 @NgModule 注释。
该问题仅在开发模式下发生。Angular 版本是 7.2.0。
因此问题是 - 必须做些什么才能使其在 AOT 模式下工作?