如果从父级重新初始化 FormGroup,自定义组件 FormControl 会中断

Posted

技术标签:

【中文标题】如果从父级重新初始化 FormGroup,自定义组件 FormControl 会中断【英文标题】:Custom component FormControl breaking if reinitializing FormGroup from parent 【发布时间】:2017-11-02 06:58:44 【问题描述】:

从自定义组件中使用的父组件重新初始化 formGroup 时遇到问题。我得到的错误是:

没有 FormControl 实例附加到表单控件元素 名称:'selectedCompany'

HTML

<form [formGroup]="addForm">
     ...
     <my-custom-component formControlName="selectedCompany"></my-custom-component>
     ...
</form

&lt;my-custom-component&gt;是按照创建自定义formControl组件的有效方式创建的:https://blog.thoughtram.io/angular/2016/07/27/custom-form-controls-in-angular-2.html#implementing-controlvalueaccessor

组件

这是初始化formGroup变量addForm的代码:

let formTemp: any = 
    selectedCompany: new FormControl(null, [Validators.required]),


this.addForm = this._formBuilder.group(formTemp);

第一次初始化addForm 一切都很好。但是当我重新打开表单所在的模态,并执行相同的组件代码时,就会出现上述错误。

【问题讨论】:

【参考方案1】:

我发现一遍又一遍地重新初始化formGroup是不好的,因为组件失去了对旧formGroup的引用。

如果需要设置值来显示新鲜的形式,.setValue 是这里的解决方案:

组件

不要重新初始化addForm,而是检查addForm是否在之前被初始化过,如果是,只为现有的FormControls设置值:

if (this.addForm) 
    this.addForm.setValue(
        selectedCountry: null
    )
 else 

    let formTemp: any = 
        selectedCompany: new FormControl(null, [Validators.required]),
    

    this.addForm = this._formBuilder.group(formTemp);

这样,我想,引用不会丢失到旧的addForm,所以不会发生错误。

【讨论】:

如果您只想更新选定数量的属性/表单控件,也可以使用 [FormGroup].patchvalue(object) Mario,非常感谢您提供此解决方案,这比@zucker 的回答(所有其他人提供的顺便说一句)要好得多,而且我相信这个解决方案通常应该被视为默认表单创建。 【参考方案2】:

我找到了一个奇怪的“解决方案”。因此,要重置子组件,这些子组件使用 formGroup 并在您将其换出时会感到困惑。我使用这个技巧。

comp.ts

public flicker: boolean = false;

reInit() 
    this.flicker = true;

    this.addForm = this._formBuilder.group(
         selectedCompany: new FormControl(null, [Validators.required]),
    );

    setTimeout( () => this.flicker = false, 200); //On very heavy pages, timeout ensures that the flicker hack works as expected.

comp.html

<form [formGroup]="addForm" *ngIf="!flicker">
     ...
     <my-custom-component formControlName="selectedCompany"></my-custom-component>
     ...
</form>

可怕的 hack,它基本上强制 formGroup 组件自行销毁并重新初始化,但绝望的时候需要绝望的措施......

我需要这个技巧,因为我使用 FormGroup 对象将表单的结果保存到临时结果数组中,每个结果都可以随意重新打开和编辑。将来我会做基于 ngModel 的表单状态保存,以避免这个问题,但这里有一个临时解决方案。

编辑 1

setTimeout( () => this.flicker = false, 200); //On very heavy pages, timeout ensures that the flicker hack works as expected.

有更好的方法来同步闪烁

this.flicker = true;
this._changeDetectorRef.detectChanges();
this.flicker = false;
this._changeDetectorRef.detectChanges();

检测更改将同步运行更改检测并更新视图,从而删除旧的表单组。

【讨论】:

是的,对于您使用反应式表单解决方案的用例。【参考方案3】:

我的解决方案是将formControlName 替换为formControl

而不是

<my-custom-component formControlName="selectedCompany"></my-custom-component>

使用

<my-custom-component [formControl]="addForm.controls['selectedCompany']"></my-custom-component>

或者用一些getMethod来获取formControl

也适用于错误:

没有 FormControl 实例附加到表单控件元素 路径

我用过一些FormArray

【讨论】:

这就是我使用新的 DateRange 和 Angular 材料的原因。谢谢

以上是关于如果从父级重新初始化 FormGroup,自定义组件 FormControl 会中断的主要内容,如果未能解决你的问题,请参考以下文章

将自定义组件添加到反应式表单的 FormGroup 的正确方法是啥?

从父级初始化子级的变量

React Native - 如何从父级获取 FlatList 项的值

如何通过事件将数据从父级传递给子级

Fancybox iframe 内容 - 如何从父级访问 CSS?

如何通过事件将数据从父级传递给子角色