Angular2 反应形式:将 FormArray 与父 FormGroup 同步

Angular2 reactive forms : syncing FormArray with parent FormGroup

我正在使用 FormArrays 来处理可变长度的对象数组。虽然我当前的实现大部分都有效,但在验证方面我遇到了问题,因为对我的 FormArray 所做的任何更改都不会传播到它的 "parent" FormGroup ......或者至少不会自动传播 FormGroup 可以通过更改由所述 FormGroup 管理的另一个控件的值来更新。

在这一点上,我无法判断我的实现是否有问题,或者我是否期待一些我不应该期待的事情......无论 "problem" 最后是什么,我此时很乐意接受任何意见。

Link 到 Plunker : https://plnkr.co/edit/PLslyJo1YOszGwTpujyx (Angular 版本为 2.0.1)

Plunker app.ts 顶部的评论详细说明了重现问题的步骤(或者无论如何对我来说是什么问题)。

由于 SO 在链接到 Plunker 时需要一些代码,这里是与 FormGroup 和 FormArray 的定义有关的行

this.form = this.fb.group({
        "size":[0, Validators.required],
        "someArray": this.fb.array(this.initFormArray(0), Validators.required),
        "someOtherField":[null]
});

initFormArray(size:number):FormGroup[]{
  let newFormArray:FormGroup[] = [] ;

  for(let i = 0 ; i < size ; i++){
    newFormArray.push(this.fb.group({
      fieldA: [null, Validators.required],
      fieldB: [null, Validators.required]
  })); 
}

  return newFormArray;
}

updateFormArray(value:string){
  let newSize:number = parseInt(value) ;

  if(newSize >= 0){
    this.form.controls["someArray"] = this.fb.array(this.initFormArray(newSize))
    this.form.updateValueAndValidity();
  }
}

这在 Plunker 中有说明,但我不妨在这里提一下,订阅 FormArray 的 valueChanges 似乎没有帮助(或者我没有在我的订阅中做正确的事情):

this.form.controls['someArray'].valueChanges.subscribe((data) => this.form.updateValueAndValidity());

感谢任何人的插话:)

编辑 (2016-10-11) :使用 valueChanges 钩子让我几乎可以解决问题,但解决方案并不令人满意。出于这个原因,我正在编辑而不是回答我自己的问题,但如果有人 mod 认为这是一个有效的结论,请务必做你认为足够的事情。

至于我的 "solution" :不是在 FormArray 或根 FormGroup 级别订阅 valueChanges,我几乎可以通过订阅每个 FormGroup 的 valueChanges observable 来实现我想要的组成 FormArray :

initFormArray(size:number):FormGroup[]{
  let newFormArray:FormGroup[] = [] ;

  for(let i = 0 ; i < size ; i++){
    let formGroup = this.fb.group({
      fieldA: [null, Validators.required],
      fieldB: [null, Validators.required]
    }) ;

    formGroup.valueChanges.subscribe((data) => this.form.updateValueAndValidity());
    newFormArray.push(formGroup); 
  }

return newFormArray;

}

通过这样做,根 FormGroup 在 FormArray 被 mod 化时更新,但有一个滞后。例如,如果我在字段 A 中输入“123”,则根 FormGroup 值包含 "fieldA":"12"

因此我不满意 "solution" :通过在订阅中添加一个虚拟的 setTimeout,根 FormGroup 完全按照我想要的方式更新......除了理想情况下不需要 setTimeout。

formGroup.valueChanges.subscribe((data) => setTimeout(()=>this.form.updateValueAndValidity(), 1);

有没有什么方法可以在不诉诸 setTimeout 的情况下进行管理?

更新的 Plunker :https://plnkr.co/edit/7mbTNPHjRhlU3ostf3av

我玩了一下你的plunker,发现了问题

当您在更改大小后分配新的 FormArray 时,您是单独分配它,没有正确地将它与父 FormGourp 相关联。
使用 this.form.controls[<controlName>] = new FormControl/ FormArray/ FormGroup; 不会将 parent 属性 应用于新控件。
这就是为什么在更新父字段之一时,直到 "refreshed" 整个表单值才将值传播到父字段。

你可以在plunker中看到,我添加了一些console.log,以便你更好地理解。

我还添加了正确的方法(在我看来)来替换 someArray FormArray 的值。
使用 official documentation 帮助很大。

希望对您有所帮助

每当嵌套表单数组发生变化时,我都会遇到更新父表单组的问题。每当表单数组更改时,尝试设置父级 属性。希望对你有帮助。

addItem(): void {
    let fa = <FormArray>this.formGroup.get(this.formProperty)
    let fb = this.CreateNewRow(this.schema);
    fb.setParent(fa);
    fa.controls.push(fb);
  }