Angular 2/4:如何在动态创建的组件上添加表单控件?

时间:2017-05-25 20:48:13

标签: angular angular2-template angular2-forms

我正在尝试使用ComponentFactoryResolver创建一个反应形式。

我想在该表单中添加的所有组件都是细节,并实现ControlValueAccessor接口。

所以,我的问题很简单:如何在不修改组件的情况下,使用ComponentFactoryResolver动态创建的组件上添加表单控件?

目前,我的代码如下:

component: ComponentRef<any>;
form: FormGroup;

@ViewChild('container', {read: ViewContainerRef}) container: ViewContainerRef;

constructor(private resolver: ComponentFactoryResolver, private fb: FormBuilder) {}

ngOnInit(): void {
  this.form = this.fb.group({});
  const component: any = MyStringComponent;
  const factory: any = this.resolver.resolveComponentFactory<any>(component);
  this.component = this.container.createComponent(factory);
}

模板:

<form
  [formGroup]="form">
  <ng-container #container>

  </ng-container>

</form>

这段代码工作正常,我的组件被注入到我想要的位置,我可以使用this.component.instance访问它的输入。

那么,我应该在哪里添加我的formControlName指令?

1 个答案:

答案 0 :(得分:0)

使用相同的用例,我发现 2020 年发布的此解决方案可以完美解决您的问题:https://stackoverflow.com/a/63523127/2879716

作者提供了 a link to StackBlitz,它展示了如何实现一个 FormControlOutletComponent 组件,该组件用作不同表单控件的单个入口点。例如,它在内部呈现一个 CustomInputComponent,您可以以任何方式实现它。唯一的要求是它应该实现 ControlValueAccessor 接口。

它增加了一层组件嵌套,因此动态组件创建实际上是在 FormControlOutletComponent 内部,而不是在您的主窗体组件中。 回答您问题的关键代码在这里:

  // in FormControlOutletComponent's decalration
  public ngOnInit(): void {
    // 1. Get NgControl reference (defined by `NG_VALUE_ACCESSOR` provider)
    const ngControl = this.injector.get(NgControl);

    // 2. Resolve dynamic component factory and create a component instance
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(CustomInputComponent);
    const componentRef = this.viewContainerRef.createComponent(componentFactory);

    // 3. Important! Delegate all value-accessor-related work to the instance of the dynamic component
    ngControl.valueAccessor = componentRef.instance;
  }

因此,您的 FormControlOutletComponent 本身成为其他动态表单组件的“代理”。

所以,要回答您最初的问题 - “我应该在哪里添加我的 formControlName 指令?”,-您应该将它(或 ngModel 指令)添加到表单的 HTML 模板中的 <app-form-control-outlet> 组件:

<form [formGroup]="form">
  <app-form-control-outlet [formControlName]="'controlName'"></app-form-control-outlet>
  <!-- of course, you can render multiple components this way using ngFor -->
</form>

请注意,FormControlOutletComponent 没有实现 ControlValueAccessor 接口,但它定义了 NG_VALUE_ACCESSOR 提供程序:

  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => FormControlOutletComponent),
      multi: true
    }
  ]

它的作用就像魔法一样。我对这个解决方案的唯一不便是我不能简单地使用标准 HTML <input/> 作为动态组件 - 我必须围绕它创建自己的包装器,带有 ControlValueAccessor 接口,它只是将所有命令代理到 <input/>。如果有人知道如何简化这一点,那就太好了。