构建 Angular2 表单 - 如何包含验证的条件表单字段

Structuring Angular2 Forms - How to include conditional form fields that validate

背景

Jecelyn Yeen excellent article 关于如何在 Angular2 中组织嵌套表单。

在她的示例中,她使用一个数组来显示多个地址表单子组件,以便用户可以无休止地单击 "Add Address" 并且表单仍然会正确验证。我想做类似的事情,但不是有一个子表单数组,而是根据下拉列表的值显示不同的子表单。

我尝试用与她类似的方式构建我的代码,除了我没有返回表单构建器数组,而是为我想显示的每个子表单使用了一个表单组。选择下拉选项时,我删除了不应显示的表单组,并重新创建应该显示的表单组。我遇到的问题是,当以这种方式构建时,父表单不会在子表单中获取更改。

代码

ts:

public showNameForm: boolean = false;
public showAddressForm: boolean = false;
public showPhoneNumberForm: boolean = false;

constructor (fb: FormBuilder) {
  this.myForm = fb.group({
    nameInfo: this.getNameForm(),
    addressInfo: this.getAddressForm(),
    phoneNumberInfo: this.getPhoneNumberForm()
  });
}

getNameForm () {
  return this.fb.group({
    name: ["", Validators.required]
  });
}

getAddressForm () {
  return this.fb.group({
    address1: ["", Validators.required],
    address2: ["", Validators.required],
    city: ["", Validators.required],
    state: ["", Validators.required],
    zipcode: ["", Validators.required]
  });
}

getPhoneNumberForm () {
  return this.fb.group({
    phone: ["", Validators.required]
  });
}

handleDropdownSelect (value) {
  if(value === 1) {
    this.showNameForm = true;

    this.myForm.controls['nameInfo'] = this.getNameForm();
    delete this.myForm.controls['addressInfo'];
    delete this.myForm.controls['phoneNumberInfo'];
  }
  // Similar for other cases, e.g. if value is 2, only show addressInfo
}

html:

<form [formGroup]="myForm">
  <div [formGroupName]="'nameInfo'" *ngIf="showNameForm">
    <input type="text" formControlName="name" />
  </div>
  <!-- Similar for other fields -->
  <!-- ... -->
  <button [ngClass]="{disabled: !myForm.valid}">Submit</button>
</form>

这样做,提交按钮总是无效,即使在我键入所有必需的文本之后也是如此。使用 Chrome 调试器检查表单对象,我发现 myForm.value 等于

{
  nameInfo: {
    name: ""
  }
}

换句话说,我在子表单组中输入的文本永远不会被父表单提取。但是我可以看到 myForm.controls.nameInfo 确实有一个包含正确信息的值,当其他人被选中时也是如此。所有这些属性的 valid 属性 为 true。然而,除非我手动调用 myForm.updateValueAndValidity(),否则父表单不会获取这些更改。这并不理想,因为我希望表单一生效就显示为有效,而不必添加我自己的事件侦听器来实现它。

根据评论,问题在于隐藏控件并且它们仍在计算验证。

如果您要隐藏和显示 select FormGroups 并且不希望它们计入验证或 form.value 属性 那么您可以调用 disable() 在单个控件上或在包含 FormGroup:

this.myForm.addressInfo.disable(); // disables the entire addressInfo group
// which removes it from validation, thus preventing false flags.