Angular 11 DI 用于动态编译的组件
Angular 11 DI for dynamically compiled components
使用 JitCompilerFactory 并在运行时编译组件是可行的(例如,请参见此处:)但注入动态组件不会:
// (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);
在组件 class 构造函数(上面的#2)中没有 DI 一切都很好,但试图注入原因:
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.
我试图根据 Angular 团队对 Angular 9 的回答在 polyfills.ts
中添加 core-js/es/reflect
和 reflect-metadata
:https://github.com/angular/angular/issues/35908 (从 AoT 编译的 Ivy 项目使用 JIT 时出现 DI 错误)。
我应该怎么做才能使 DI 用于 Angular 11 中的动态组件?
回答我的问题以防有人在做同样的事情。感谢那些在 Angular 问题跟踪器上回复的人:https://github.com/angular/angular/issues/41933
DI 失败,因为 TS 编译远远早于我们在运行时创建组件 class 并且 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;
})();
使用 JitCompilerFactory 并在运行时编译组件是可行的(例如,请参见此处:
// (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);
在组件 class 构造函数(上面的#2)中没有 DI 一切都很好,但试图注入原因:
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.
我试图根据 Angular 团队对 Angular 9 的回答在 polyfills.ts
中添加 core-js/es/reflect
和 reflect-metadata
:https://github.com/angular/angular/issues/35908 (从 AoT 编译的 Ivy 项目使用 JIT 时出现 DI 错误)。
我应该怎么做才能使 DI 用于 Angular 11 中的动态组件?
回答我的问题以防有人在做同样的事情。感谢那些在 Angular 问题跟踪器上回复的人:https://github.com/angular/angular/issues/41933
DI 失败,因为 TS 编译远远早于我们在运行时创建组件 class 并且 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;
})();