将 angular FormBuilder 服务注入动态组件的方法
Way to inject angular FormBuilder service to dynamic component
我正在尝试以这种方式将 FormBuilder 服务注入动态组件:
模板:
...
<div #vc></div>
...
分量:
@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);
})
}
这样做,我得到了这个错误:
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
尝试添加自定义注入器没有帮助:
let injector1 = Injector.create([
{
provide: 'FormBuilder',
useValue: FormBuilder
}
]);
这是重现该问题的 Stackblitz:
angular-dynamic-components-example
有什么办法可以解决这个问题吗?
?
登录错误,如 Can't resolve all parameters for class_1: (?)
意味着 Angular 无法解析传递给构造函数的参数类型。换句话说,反射器无法识别 private fb: FormBuilder
参数具有 FormBuilder
类型,因为类型在 TypeScript 编译后消失。
为了告诉 TS 编译器它应该保留此类型,您需要将此 class 定义重写为带有装饰器的版本,例如:
@Component({
template: template,
styles: [
`
label {
width: 128px;
margin: 0px 8px;
}
`
]
})
class tmpCmp {
constructor(private fb: FormBuilder) {}
}
这将编译为:
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);
你可以注意到 __metadata("design:paramtypes"
部分负责向 Angular 反射器提供信息。
还有其他方法可以解决。
静态参数
const tmpCmp = Component({
...
})(
class {
constructor(private fb: FormBuilder) {}
static parameters = [ FormBuilder ]
}
);
静态 ctorParameters 方法
const tmpCmp = Component({
...
})(
class {
constructor(private fb: FormBuilder) {}
static ctorParameters = () => [{ type: FormBuilder} ]
}
);
我正在尝试以这种方式将 FormBuilder 服务注入动态组件:
模板:
...
<div #vc></div>
...
分量:
@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);
})
}
这样做,我得到了这个错误:
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
尝试添加自定义注入器没有帮助:
let injector1 = Injector.create([
{
provide: 'FormBuilder',
useValue: FormBuilder
}
]);
这是重现该问题的 Stackblitz:
angular-dynamic-components-example
有什么办法可以解决这个问题吗?
?
登录错误,如 Can't resolve all parameters for class_1: (?)
意味着 Angular 无法解析传递给构造函数的参数类型。换句话说,反射器无法识别 private fb: FormBuilder
参数具有 FormBuilder
类型,因为类型在 TypeScript 编译后消失。
为了告诉 TS 编译器它应该保留此类型,您需要将此 class 定义重写为带有装饰器的版本,例如:
@Component({
template: template,
styles: [
`
label {
width: 128px;
margin: 0px 8px;
}
`
]
})
class tmpCmp {
constructor(private fb: FormBuilder) {}
}
这将编译为:
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);
你可以注意到 __metadata("design:paramtypes"
部分负责向 Angular 反射器提供信息。
还有其他方法可以解决。
静态参数
const tmpCmp = Component({
...
})(
class {
constructor(private fb: FormBuilder) {}
static parameters = [ FormBuilder ]
}
);
静态 ctorParameters 方法
const tmpCmp = Component({
...
})(
class {
constructor(private fb: FormBuilder) {}
static ctorParameters = () => [{ type: FormBuilder} ]
}
);