简体   繁体   中英

Angular2 reactive forms : syncing FormArray with parent FormGroup

I am using FormArrays to deal with object arrays of variable lengths. While my current implementation mostly works, I have a problem when it comes to validation, because any changes made to my FormArray is not propagated to its "parent" FormGroup... or at least not automatically The value of the FormGroup can be updated, by changing the value of another control managed by said FormGroup.

At this point, I can't tell if there is something wrong with my implementation, or if I'm expecting something I shouldn't... Whatever the "problem" is in the end, I'll gladly take any input at this point.

Link to the Plunker : https://plnkr.co/edit/PLslyJo1YOszGwTpujyx (Angular version is 2.0.1)

The comment at the top of app.ts in the Plunker details the steps to reproduce the problem (or what is a problem to me, anyway).

As SO asks for some code when linking to Plunker, here are the lines pertaining to the definition of the FormGroup and 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();
  }
}

This is stated in the Plunker, but I might as well mention here that subscribing to the valueChanges of the FormArray does not seem to help (or maybe I didn't do the right thing inside my subscription) :

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

Thanks to anyone chiming in :)

EDIT (2016-10-11) : playing around with the valueChanges hook some more allowed me to virtually solve the problem, but with an unsatisfactory solution. For this reason, I'm editing rather than answering my own question, but if any mod considers this to be a valid conclusion, by all means please do what you deem adequate.

As for my "solution" : instead of subscribing to valueChanges at FormArray or root FormGroup level, I could almost achieve what I wanted by subscribing to the valueChanges observable of each FormGroup that compose the 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;

}

By doing so, the root FormGroup is updated when the FormArray is modified, but with a lag. For instance, if I input "123" in field A, the root FormGroup value contains "fieldA":"12" .

Hence my unsatisfactory "solution" : by adding a dummy setTimeout in the subscription, the root FormGroup updates exactly as I want it to... except ideally there would be no need for a setTimeout.

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

Is there any way to manage this without resorting to setTimeout ?

The updated Plunker : https://plnkr.co/edit/7mbTNPHjRhlU3ostf3av

I played somewhat with your plunker, and found out the problem

When you assigned the new FormArray after changing the size of it, you assigned it alone, not relating it correctly to the parent FormGourp .
Using this.form.controls[<controlName>] = new FormControl/ FormArray/ FormGroup; does not apply the parent property to the new control.
That's why the values did not propagate to the parent until you "refreshed" the entire form value when updating one of the parent fields.

You can see in the plunker , I added some console.log s for you to better understand.

I also added the correct way (in my opinion) to replace the value of the someArray FormArray.
Using the official documentation helped a lot.

Hope this helps

I have the same issue of updating parent formgroup whenever nested form array changes. Try to set parent property whenever form array changes. Hope it helps.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM