我正在尝试使用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指令?
答案 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/>
。如果有人知道如何简化这一点,那就太好了。