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

问题描述

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

分叉 Stackblitz

这将被编译为:

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

分叉 Stackblitz

静态ctorParameters方法

const tmpCmp = Component({...})(班级 {构造函数(私有 fb:FormBuilder){}静态ctorParameters = () =>[{ 类型: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

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

09-25 12:13