简体   繁体   中英

Angular 11 DI for dynamically compiled components

Using JitCompilerFactory and compiling components at runtime works (for example see here: https://stackoverflow.com/a/67122569/15816951 ) but injecting into dynamic component does not:

// (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);

Without the DI in component class constructor (#2 above) all is fine but trying to inject causes:

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.
Please check that 1) the type for the parameter at index 0 is correct and 2) the correct Angular decorators are defined for this class and its ancestors. 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.

I tried to add core-js/es/reflect and reflect-metadata in polyfills.ts based on this answer from Angular team for Angular 9: https://github.com/angular/angular/issues/35908 (DI error when using JIT from an AoT-compiled Ivy project).

What should I do to make DI work for dynamic components in Angular 11?

Answering my question in case someone is after same thing. Thanks to those who replied on Angular issue tracker: https://github.com/angular/angular/issues/41933

DI fails because TS compilation is way before at point where we create component class at runtime and Angular has no metadata information available for the constructor. Sou you can either:

Provide injection metadata manually like:

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

Or add a decorator and call it in an IIFE like:

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;
 })();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM