首页 > 解决方案 > Angular 11 DI 用于动态编译的组件

问题描述

使用 JitCompilerFactory 并在运行时编译组件有效(例如,请参见此处: https ://stackoverflow.com/a/67122569/15816951 )但注入动态组件不会:

// (1) define Component metadata
const metadata = new Component({
  selector: "dynamic-selector",
  template: "This is template: {{text}}"    // <---- Interpolation: works
});

// (2) define Component class and decorate
const compClass = class DynamicComponent {
   text: string = 'from';                   // <---- Interpolation: works
   constructor(public s: Service1) { }      // <---- Trying to inject a service: FAILS
};
const decoratedComp = Component(metadata)(compClass);

// (3) define Module class and decorate
const decoratedModule = NgModule({
   imports: [...],
   declarations: [decoratedCmp]
})(class DynamicModule { }) 

// (4 )compile Module and grab the Component Factory
const module  = compiler.compileModuleAndAllComponentsAsync(decoratedModule);
const factory = module.componentFactories.find(x => x.componentType === decoratedCmp);

// (5) render the component using Component Factory
someViewContainerRef.createComponent(factory);

如果没有组件类构造函数中的 DI(上面的#2),一切都很好,但尝试注入原因:

core.js:6210 错误错误:未捕获(承诺中):错误:此构造函数与 Angular 依赖注入不兼容,因为它在参数列表索引 0 处的依赖无效。
如果依赖类型是像字符串这样的原始类型,或者如果此类的祖先缺少 Angular 装饰器,则可能会发生这种情况。
请检查 1) 索引 0 处的参数类型是否正确,以及 2) 为此类及其祖先定义了正确的 Angular 装饰器。错误:此构造函数与 Angular 依赖注入不兼容,因为它在参数列表索引 0 处的依赖无效。如果依赖类型是像字符串这样的原始类型,或者如果此类的祖先缺少 Angular 装饰器,则可能会发生这种情况。

我尝试根据 Angular 团队对 Angular 9 的回答添加core-js/es/reflectreflect-metadata加入: https ://github.com/angular/angular/issues/35908 (使用 AoT 编译的 Ivy 项目中的 JIT 时出现 DI 错误)。polyfills.ts

我应该怎么做才能使 DI 为 Angular 11 中的动态组件工作?

标签: angulardependency-injectionangular-ivydynamic-compilation

解决方案


回答我的问题,以防有人追求同样的事情。感谢那些在 Angular 问题跟踪器上回复的人:https ://github.com/angular/angular/issues/41933

DI 失败是因为 TS 编译早于我们在运行时创建组件类的时间,并且 Angular 没有可用于构造函数的元数据信息。你可以:

手动提供注入元数据,例如:

(cmpClass as any).ctorParameters = () => [ {type: Service1 }];

或者添加一个装饰器并在 IIFE 中调用它,例如:

export function Annotate() {
   return klass => klass;
}

const cmpClass = (() => {
   @Annotate()
   class DynamicComponent {
     text: string = "Text";

     constructor(public s: Service1) {
       console.log(s);
     }

     handle() {
       this.text = "Event handler works!";
     }
   }
   return DynamicComponent;
 })();

推荐阅读