且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

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

更新时间:2023-11-24 10:45:28

有同样的用例,我发现这个在 2020 年发布的解决方案可以完美解决您的问题:https://***.com/a/63523127/2879716

Having the same use-case, I found this solution posted in 2020 to perfectly solve your issue: https://***.com/a/63523127/2879716

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

The author provides a link to StackBlitz which shows how you can implement a FormControlOutletComponent component which serves as a single entry point for different form controls. As an example, it renders a CustomInputComponent inside, which can be implemented any way you want. The only requirement is that it should implement ControlValueAccessor interface.

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

It adds one more layer of components nesting, so dynamic component creation actually goes inside of FormControlOutletComponent, not in your main form component. The key code which answers your question is here:

  // 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 本身就变成了代理"用于其他动态表单组件.

As a result, your FormControlOutletComponent becomes itself a "proxy" for other dynamic form components.

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

So, to answer your original question - "where should I add my formControlName directive?", - you should add it (or ngModel directive) to the <app-form-control-outlet> component in your form's HTML template:

<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 提供者:

Note that FormControlOutletComponent doesn't implement ControlValueAccessor interface, though it defines NG_VALUE_ACCESSOR provider:

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

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

It works like a magic. The only inconvenience I'm having with this solution is that I can't simply use standard HTML <input/> as a dynamic component - I have to create my own wrapper around it, with ControlValueAccessor interface, which simply proxies all the commands to the <input/>. If somebody knows how to simplify this, it would be great.