将角度FormBuilder服务注入动态组件的方法 [英] Way to inject angular FormBuilder service to dynamic component

本文介绍了将角度FormBuilder服务注入动态组件的方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试通过以下方式将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){}} 

分叉的Stackblitz

它将被编译为:

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

分叉的Stackblitz

静态ctorParameters方法

  const tmpCmp = Component({...})(班级 {构造函数(私有fb:FormBuilder){}static ctorParameters =()=>[{type:FormBuilder}]}); 

分叉的Stackblitz

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) {}
}

Forked Stackblitz

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

Forked Stackblitz

Static ctorParameters method

const tmpCmp = Component({
 ...
})(
  class {
    constructor(private fb: FormBuilder) {}

    static ctorParameters = () => [{ type: FormBuilder} ]
  }
);

Forked Stackblitz

这篇关于将角度FormBuilder服务注入动态组件的方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆