【问题标题】:How to compile runtime-generated Angular8 code?如何编译运行时生成的 Angular8 代码?
【发布时间】:2020-07-23 01:28:13
【问题描述】:

我在运行时创建 Angular 代码,特别是,我使用 SVG 库来创建一个矢量图形,其中包含像 (click)='myMethod()' 这样的 Angular 代码指令,而这些指令又调用我在SVG 封装组件。在运行时生成,我需要编译创建的模板并将其添加到组件中。我当时在 this post 的帮助下使用 Angular 3 实现了这样的代码,这非常麻烦。我尝试在 Angular 8 应用中复制旧代码:

private addComponent(template: string) {

    @Component({template: template + ' <div #target></div>'})
    class TemplateComponent {

      @ViewChild('target', {static: false, read: ViewContainerRef}) public target;

      constructor() {
      }

      public myMethod() {
        // do something     
      }
    }

    @NgModule({declarations: [TemplateComponent]})
    class TemplateModule {
      @ViewChild('target', {static: false, read: ViewContainerRef}) public target;
    }

    // ERROR in next line:
    const mod = this.compiler.compileModuleAndAllComponentsSync(TemplateModule);
    const factory = mod.componentFactories.find((comp) =>
        comp.componentType === TemplateComponent
    );
    this.container.createComponent(factory);
}

现在失败了

ERROR 错误:未加载运行时编译器 在 Compiler._throwError (core.js:38932)

一方面,我不知道为什么会发生该错误。我在互联网上找到的所有内容都是关于 critical function syntax 在延迟模块加载中的。另一方面,我想知道,如果以后有五个 Angular 主要版本,还有其他方法可以做到这一点。我读到了Portals,但它似乎是关于动态加载静态模板,而不是动态生成的未编译模板。期待有人指出我正确的方向。

Post Scriptum: 为可以在 AoT 模式下提供针对 Angular v9 的基本运行代码 sn-p 的人添加赏金。它必须包含一个运行时编译的模板(例如,来自字符串变量),其中包含关联组件中的方法调用。

【问题讨论】:

  • 你用 Angular 3 实现了那种代码? :-)
  • 简单的解决方法是禁用 aot,但我怀疑这是否适合您?
  • @David,是的,非常好的提示。我应该在 AoT 工作。我将其添加到帖子中。
  • 你能展示你的路由模块吗?
  • @nafeo 模块是这样加载的 {path: 'user', loadChildren: () =&gt; import('./pages/user/user.module').then(m =&gt; m.UserModule), data: {breadcrumb: 'User'}}

标签: angular angular8 dynamic-loading angular-compiler


【解决方案1】:

JIT 编译器现在默认从 AOT 包中排除,因此您必须手动包含它。您需要安装包@angular/platform-browser-dynamic 并将Compiler 提供程序添加到您的应用程序模块。例如:

import { NgModule, COMPILER_OPTIONS, CompilerFactory, Compiler } from '@angular/core';
import { JitCompilerFactory } from '@angular/platform-browser-dynamic';

@NgModule({
  providers: [
    { provide: COMPILER_OPTIONS, useValue: {}, multi: true },
    { provide: CompilerFactory, useClass: JitCompilerFactory, deps: [COMPILER_OPTIONS] },
    { provide: Compiler, useFactory: createCompiler, deps: [CompilerFactory] }
  ]
})
export class AppModule { }

export function createCompiler(compilerFactory: CompilerFactory) {
  return compilerFactory.createCompiler();
}

然后您还必须对代码进行一些小的更改,因为您无法使用 @Component 装饰器将模板设置为变量,这会导致编译错误。装饰器必须在代码中动态运行。

以下是您的方法更新后的更改:

private addComponent(template: string) {
  class TemplateComponent {

    @ViewChild('target', {static: false, read: ViewContainerRef}) public target;

    constructor() {
    }

    public myMethod() {
      // do something     
    }
  }

  class TemplateModule {
    @ViewChild('target', {static: false, read: ViewContainerRef}) public target;
  }

  const componentType = Component({template: template + '<div #target></div>'})(TemplateComponent)

  const componentModuleType = NgModule({declarations: [componentType]})(TemplateModule)

  const mod = this.compiler.compileModuleAndAllComponentsSync(componentModuleType);
  const factory = mod.componentFactories.find((comp) =>
      comp.componentType === componentType
  );
  this.container.createComponent(factory);
}

我还创建了一个StackBlitz sample,您可以在其中看到它的工作原理。

有关如何执行此操作的更详细示例,请查看此Angular AOT Dynamic Components GitHub repository 完整示例。

我在Angular GitHub issue about dynamically loading component templates 中找到了这个。由于您使用它,因此您最好关注该问题以了解最新进展。

【讨论】:

  • 您可能需要在angular.json 中设置buildOptimizer : false,否则在prod 模式下将不起作用
  • @David,感谢您的提示。当我们将代码投入生产时会牢记这一点。
  • 请注意动态模块导入(例如CommonModule)仅适用于angular.json中的aot: false
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多