问题描述
我正在尝试以这种方式将 FormBuilder 服务注入到动态组件中:
模板:
...<div #vc></div>...
组件:
@ViewChild('vc', { read: ViewContainerRef }) _container: ViewContainerRef;...构造函数(私人FB:FormBuilder,私有 componentFactoryResolver: ComponentFactoryResolver,私有_compiler:编译器,私有_injector:注入器,私有_m:NgModuleRef有什么办法可以解决这个问题吗?
解决方案 ?
登录错误如 Can't resolve all parameters for class_1: (?)
表示Angular 无法解析传递给构造函数的参数类型.换句话说,反射器无法识别 private fb: FormBuilder
参数具有 FormBuilder
类型,因为 TypeScript 编译后类型消失.
为了告诉 TS 编译器它应该保留这种类型,您需要将此类定义重写为带有装饰器的版本,例如:
@Component({模板:模板,样式: [`标签 {宽度:128px;边距:0px 8px;}`]})类 tmpCmp {构造函数(私有 fb:FormBuilder){}}
这将被编译为:
tmpCmp = __decorate([core_1.Component({模板:模板,样式: [`标签 {宽度:128px;边距:0px 8px;}`]}),__metadata("design:paramtypes", [typeof (_a = typeof forms_1.FormBuilder !== "undefined" & forms_1.FormBuilder) === "function" ? _a : Object])], tmpCmp);
你可以注意到 __metadata("design:paramtypes"
部分,它负责向 Angular 反射器提供信息.
还有其他解决方法.
静态参数
const tmpCmp = Component({...})(班级 {构造函数(私有 fb:FormBuilder){}静态参数 = [ FormBuilder ]});
静态ctorParameters方法
const tmpCmp = Component({...})(班级 {构造函数(私有 fb:FormBuilder){}静态ctorParameters = () =>[{ 类型:FormBuilder}]});
I'm trying to inject the FormBuilder service to a dynamic component this way:
Template:
...
<div #vc></div>
...
Component:
@ViewChild('vc', { read: ViewContainerRef }) _container: ViewContainerRef;
...
constructor(private fb: FormBuilder,
private componentFactoryResolver: ComponentFactoryResolver,
private _compiler: Compiler, private _injector: Injector,
private _m: NgModuleRef<any>) {
}
...
ngAfterViewInit() {
let allPms: any[] = null;
let template = '';
// construct template on the fly
const wTypes = this._f.w_type;
for (const plug of this._plugs) {
if (plug.name === wTypes) {
allPms = plug.params;
}
}
for (const pm of allPms) {
if (pm.type === 'str') {
template = template + `
<div class="form-group row">
<label class="col-sm-3 col-form-label"><strong>` + pm.name + `</strong></label>
<div class="col-sm-8">
<input class="form-control" name="` + pm.name + `" type="text"
formControlName="` + pm.name + `">
</div>
</div>
`;
}
}
// add field for each pm
let injector1 = Injector.create([
{
provide: 'FormBuilder',
useValue: FormBuilder
}
]);
const tmpCmp = Component({ template: template, styles: [`label {
width: 128px;
margin: 0px 8px;
}`] })(class {
constructor(private fb: FormBuilder) {
}
});
const tmpModule = NgModule({ declarations: [tmpCmp] })(class {
});
this._compiler.compileModuleAndAllComponentsAsync(tmpModule)
.then((factories) => {
const f = factories.componentFactories[0];
this.cmpRef = f.create(injector1, [], null, this._m);
this.cmpRef.instance.name = 'B component';
this._container.insert(this.cmpRef.hostView);
})
}
Doing so, I got this error:
ERROR Error: Can't resolve all parameters for class_1: (?).
at syntaxError (compiler.js:1021)
at CompileMetadataResolver.push../node_modules/@angular/compiler/fesm5/compiler.js.CompileMetadataResolver._getDependenciesMetadata (compiler.js:10922)
at CompileMetadataResolver.push../node_modules/@angular/compiler/fesm5/compiler.js.CompileMetadataResolver._getTypeMetadata (compiler.js:10815)
at CompileMetadataResolver.push../node_modules/@angular/compiler/fesm5/compiler.js.CompileMetadataResolver.getNonNormalizedDirectiveMetadata (compiler.js:10434)
at CompileMetadataResolver.push../node_modules/@angular/compiler/fesm5/compiler.js.CompileMetadataResolver.loadDirectiveMetadata (compiler.js:10296)
at compiler.js:23883
at Array.forEach (<anonymous>)
at compiler.js:23882
at Array.forEach (<anonymous>)
at JitCompiler.push../node_modules/@angular/compiler/fesm5/compiler.js.JitCompiler._loadModules (compiler.js:23879)
View_testComponent_17 @ testComponent.html:72
push../node_modules/@angular/core/fesm5/core.js.DebugContext_.logError @ core.js:11306
push../node_modules/@angular/core/fesm5/core.js.ErrorHandler.handleError @ core.js:1719
(anonymous) @ core.js:4578
./node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke @ zone.js:391
./node_modules/zone.js/dist/zone.js.Zone.run @ zone.js:150
push../node_modules/@angular/core/fesm5/core.js.NgZone.runOutsideAngular @ core.js:3779
push../node_modules/@angular/core/fesm5/core.js.ApplicationRef.tick @ core.js:4578
(anonymous) @ core.js:4462
./node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke @ zone.js:391
onInvoke @ core.js:3820
Trying to add a custom injector did not help:
let injector1 = Injector.create([
{
provide: 'FormBuilder',
useValue: FormBuilder
}
]);
Here is the Stackblitz that reproduces the issue:
angular-dynamic-components-example
Is there any way to resolve this issue ?
解决方案 The ?
sign in errors like Can't resolve all parameters for class_1: (?)
means that Angular can't resolve type of parameter passed to constructor. In other words, reflector can't recognize that private fb: FormBuilder
parameter has FormBuilder
type because type dissappers after TypeScript compilation.
In order to tell TS compiler that it should keep this type you need to rewrite this class definition to version with decorator like:
@Component({
template: template,
styles: [
`
label {
width: 128px;
margin: 0px 8px;
}
`
]
})
class tmpCmp {
constructor(private fb: FormBuilder) {}
}
This will be compiled to:
tmpCmp = __decorate([
core_1.Component({
template: template,
styles: [
`
label {
width: 128px;
margin: 0px 8px;
}
`
]
}),
__metadata("design:paramtypes", [typeof (_a = typeof forms_1.FormBuilder !== "undefined" && forms_1.FormBuilder) === "function" ? _a : Object])
], tmpCmp);
where you can notice __metadata("design:paramtypes"
part which is responsible for providing information to Angular reflector.
There are other ways of solving it.
Static parameters
const tmpCmp = Component({
...
})(
class {
constructor(private fb: FormBuilder) {}
static parameters = [ FormBuilder ]
}
);
Static ctorParameters method
const tmpCmp = Component({
...
})(
class {
constructor(private fb: FormBuilder) {}
static ctorParameters = () => [{ type: FormBuilder} ]
}
);
这篇关于将 Angular FormBuilder 服务注入动态组件的方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!