从父组件更改嵌套表单字段值

Change nested form field value from parent component

假设我有一个实现 CVA(控制值访问器)的嵌套表单。

// Child component which implements CVA
this.addressForm = new FormGroup({
 city: new FormControl()
 postalCode: new FormControl(null)
})
// Parent component html
<form [formGroup]= "productForm">
 <input formControlName="date">
 <address-form formControlName="addressForm"> </address-form> 
</form>

// Parent component ts
 this.productForm = new FormGroup({
  date: new FormControl(null),
  addressForm: new FormControl(null)
});

现在我只想修补 addresForm 的 city 字段,所以我尝试了:

// This will set other values of addressForm to null.
this.productForm.get('addressForm').patchValue({city: 'some-city'});

// This below will not work.
this.productForm.get('addressForm.city').patchValue('some-city');// error no city control.

// Another workaround is using ControlContainer instead to link to parent
Instead of this:
  // this.addressForm = new FormGroup({
  // city: new FormControl()
  // postalCode: new FormControl(null)
  //})
using this on child
this.addressForm = this.controlContainer.control as FormGroup;
and declare formGroup on Parent.
this.productForm = new FormGroup({
  date: new FormControl(null),
  addressForm: new FormGroup({city: new FormControl(null), postalCode: new FormControl(null) })
});

BUT in this workaround, writeValue method is not called on Child.

由于 formControlName 包含单个值,因此其他值将被重置。 那么如何在不重置其他字段值的情况下设置嵌套表单字段值呢?

换句话说,我们如何控制来自父级的嵌套表单组件字段,例如修补值或监听特定字段值的变化? Stackblitz URL

庸俗,你没有一个formGroup里面的formGroup,你只有一个FormGroup和两个FormControl,最后一个存储一个object.

一般来说,当我们使用 FormGroup 时,我们可以采用两种方法

1.-在parent中定义完整的FormGroup,并将内部FormGroup

传递给children
this.productForm = new FormGroup({
  date: new FormControl(null),
  addressForm: new FormGroup({ //see that adressForm is a FormGroup
     city: new FormControl()
     postalCode: new FormControl(null)
  })
});

我们的 child 是一个简单的组件 - 没有实现 controlValue 访问器

//declare a variable "form"
form:FormGroup
//in a input asign the variale to the form
@Input('addressForm') _ set(value)
{
   this.form=value as FormGroup
}

<!--we control the formGroup as another formGroup-->
<form [formGroup]="form">
  <input formControlName="city">
  <input formControlName="postalCode">
</form>

我们在parent

中使用
<form [formGroup]= "productForm">
 <input formControlName="date">
 <!--see that pass as @Input the formGroup-->
 <address-form [addressForm]="productForm.get('addressForm')"> </address-form> 
</form>

2.-创建一个自定义表单控件来管理 object {city:..postalCode:..}

export class CustomAddressComponent implements ControlValueAccessor,OnDestroy {
  form: FormGroup = new FormGroup({
    city: new FormControl(),
    postalCode: new FormControl(null)
  });
  subscription:any
  onChange = (obj: any) => {};

  onTouched = () => {};
  writeValue(obj: any) {
    this.form.patchValue(obj);
    this.subscription=this.form.valueChanges.subscribe(res=>this.onChange(res))
  }
  registerOnChange(onChange: any) {
    this.onChange = onChange;
  }

  registerOnTouched(onTouched: any) {
    this.onTouched = onTouched;
  }
  setDisabledState(disabled: boolean) {
    this.form[disabled ? 'disable' : 'enable']();
  }
  ngOnDestroy()
  {
    this.subscription.unsubscribe()
  }

并在parent中用作

  productForm = new FormGroup({
    date: new FormControl(null),
    addressForm: new FormControl(null)  //<--see that is only a FormControl
  });

<form [formGroup]="productForm2">
  <input formControlName="date">
  <!--use formControlName, it's like another input!-->
  <custom-address-form formControlName="addressForm"> </custom-address-form>
</form>

stackblitz 你有两种方法。看到 form.value 在两种情况下都具有相同的结构

注意:实际上,要管理 FormGroup,我更喜欢使用简单的方法(使用组件),与组 FormControls 相比,仅使用自定义表单控件“更多”。

NOTE2:自定义表单控件快速制作只为demo提案

Update 确实还有其他方法,我们可以,例如创建我们的表单

  productForm = new FormGroup({
    date: new FormControl(null),
    addressForm: new FormGroup({}) //<--an empty FormGroup
  });

我们的组件创建表单

  form:FormGroup
  @Input('addressForm') set _(value:any)
  {
     this.form=value as FormGroup;
     this.form.addControl('city',new FormControl())
     this.form.addControl('postalCode',new FormControl())
  }

甚至使用 FormGroupDirective 访问一个表单并创建一个类似

的表单
  productForm3 = new FormGroup({
    date: new FormControl(null), //<--see that we don't add anything more
  });

并使用像

这样的组件
  form: FormGroup;
  constructor(
    @Host() private formGroupDirective: FormGroupDirective,
    @Attribute('groupName') private name
  ) {}
  ngOnInit() {
    setTimeout(() => {
      this.formGroupDirective.form.addControl(
        this.name,
        new FormGroup({
          city: new FormControl(),
          postalCode: new FormControl()
        })
      );
      console.log(this.formGroupDirective.form.value);
      this.form = this.formGroupDirective.form.get(this.name) as FormGroup;
    });
  }

而我们的 parent 组件:

<form [formGroup]="productForm3">
  <input formControlName="date">
  <!--see that only put our component yet add the formGroup-->
  <address-form-three groupName='addressForm'> </address-form-three>
</form>

我用这两个案例更新了 stackblitz

注意:我想记住另一种方式,但我找不到 link :(