1

免责声明:这个问题的灵感来自于 Medium 上的“ Here is what you need to know about dynamic components in Angular ”。

有一位作者展示了如何动态生成模块和组件,将后者放入前者,动态编译模块,获取组件工厂并使用它通过ViewContainerRef生成组件。

这在基本设置中工作得很好。

现在让我们假设我们想要动态生成一个组件,但这次使用的是使用异步管道的模板。

接下来,我们需要让这个东西在生产环境中工作,这意味着两件事:

由于 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 模式下工作?

4

0 回答 0