将角度FormBuilder服务注入动态组件的方法 [英] Way to inject angular FormBuilder service to dynamic component
问题描述
我正在尝试通过以下方式将FormBuilder服务注入到动态组件中:
模板:
<代码> ...< div#vc></div>...
组件:
<代码>@ViewChild('vc',{阅读:ViewContainerRef})_container:ViewContainerRef;...构造函数(私有fb:FormBuilder,私有componentFactoryResolver:ComponentFactoryResolver,private _compiler:编译器,private _injector:注入器,私人_m:NgModuleRef< any>){}...ngAfterViewInit(){让allPms:any [] = null;让template ='';//即时构建模板const wTypes = this._f.w_type;用于(this._plugs的const插件){如果(plug.name === wTypes){allPms = plug.params;}}表示(allPms的pm常量){如果(pm.type ==='str'){模板=模板+`< div class =表单组行">< label class ="col-sm-3 col-form-label"< strong>`+ pm.name +`</strong></label>< div class ="col-sm-8"><输入类别=表单控制";name ="`+ pm.name +`"type ="text"formControlName =" + pm.name +`"></div></div>`;}}//为每个下午添加字段让jector1 = Injector.create([{提供:"FormBuilder",useValue:FormBuilder}]);const tmpCmp = Component({template:template,styles:[`label {宽度:128像素;边距:0px 8px;}`]})(class {构造函数(私有fb:FormBuilder){}});const tmpModule = NgModule({声明:[tmpCmp]})(class {});this._compiler.compileModuleAndAllComponentsAsync(tmpModule).then((factories)=> {const f = factory.componentFactories [0];this.cmpRef = f.create(injector1,[],null,this._m);this.cmpRef.instance.name ='B组件';this._container.insert(this.cmpRef.hostView);})}
这样做,我得到了这个错误:
错误错误:无法解析class_1的所有参数:(?).在语法错误(compiler.js:1021)在CompileMetadataResolver.push ../node_modules/@angular/compiler/fesm5/compiler.js.CompileMetadataResolver._getDependenciesMetadata(compiler.js:10922)在CompileMetadataResolver.push ../node_modules/@angular/compiler/fesm5/compiler.js.CompileMetadataResolver._getTypeMetadata(compiler.js:10815)在CompileMetadataResolver.push ../node_modules/@angular/compiler/fesm5/compiler.js.CompileMetadataResolver.getNonNormalizedDirectiveMetadata(compiler.js:10434)在CompileMetadataResolver.push ../node_modules/@angular/compiler/fesm5/compiler.js.CompileMetadataResolver.loadDirectiveMetadata(compiler.js:10296)在compile.js:23883在Array.forEach(< anonymous>)在compile.js:23882在Array.forEach(< anonymous>)在JitCompiler.push ../node_modules/@angular/compiler/fesm5/compiler.js.JitCompiler._loadModules(compiler.js:23879)View_testComponent_17 @ testComponent.html:72push ../node_modules/@angular/core/fesm5/core.js.DebugContext_.logError @ core.js:11306push ../node_modules/@angular/core/fesm5/core.js.ErrorHandler.handleError @ core.js:1719(匿名)@ 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:150push ../node_modules/@angular/core/fesm5/core.js.NgZone.runOutsideAngular @ core.js:3779push ../node_modules/@angular/core/fesm5/core.js.ApplicationRef.tick @ core.js:4578(匿名)@ core.js:4462./node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke @ zone.js:391onInvoke @ core.js:3820
尝试添加自定义注射器没有帮助:
让jector1 = Injector.create([{提供:"FormBuilder",useValue:FormBuilder}]);
这是
有什么办法可以解决此问题?
?
登录错误,例如无法解析class_1的所有参数:(?)
意味着Angular无法解析传递给构造函数的参数类型.换句话说,反射器无法识别 private fb:FormBuilder
参数具有 FormBuilder
类型,因为在TypeScript编译后类型会消失.
为了告诉TS编译器应保留此类型,您需要使用装饰器将此类定义重写为版本,例如:
@Component({模板:模板,样式:[`标签 {宽度:128像素;边距:0px 8px;}`]})类tmpCmp {构造函数(私有fb:FormBuilder){}}
它将被编译为:
tmpCmp = __decorate([core_1.Component({模板:模板,样式:[`标签 {宽度:128像素;边距: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){}static ctorParameters =()=>[{type: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} ]
}
);
这篇关于将角度FormBuilder服务注入动态组件的方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!