代码之家  ›  专栏  ›  技术社区  ›  HDJEMAI

将FormBuilder注入动态组件的方法

  •  0
  • HDJEMAI  · 技术社区  · 5 年前

    我试图通过以下方式将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

    enter image description here

    有没有办法解决这个问题?

    0 回复  |  直到 5 年前
        1
  •  1
  •   yurzui    5 年前

    这个 ? 登录错误,如 Can't resolve all parameters for class_1: (?) 意味着Angular无法解析传递给构造函数的参数类型。换句话说,reflector无法识别这一点 private fb: FormBuilder 参数有 FormBuilder 类型,因为类型在类型脚本编译后消失。

    为了告诉TS编译器它应该保持这种类型,您需要用decorator将这个类定义重写为以下版本:

    @Component({
      template: template,
      styles: [
        `
          label {
            width: 128px;
            margin: 0px 8px;
          }
        `
      ]
    })
    class tmpCmp {
      constructor(private fb: FormBuilder) {}
    }
    

    Forked Stackblitz

    这将被汇编为:

    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" 负责向角反射器提供信息的部件。

    还有其他解决方法。

    静态参数

    const tmpCmp = Component({
      ...
    })(
      class {
        constructor(private fb: FormBuilder) {}
    
        static parameters = [ FormBuilder ]
      }
    );
    

    Forked Stackblitz

    静态参数法

    const tmpCmp = Component({
     ...
    })(
      class {
        constructor(private fb: FormBuilder) {}
    
        static ctorParameters = () => [{ type: FormBuilder} ]
      }
    );
    

    Forked Stackblitz

    推荐文章