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
The FormArray is a way to Manage collection of Form controls in Angular. The controls can be FormGroup, FormControl, or another FormArray. Because it is implemented as Array, it makes it easier dynamically add controls.
We do not have a name to the FormGroup . The Index of the element is automatically assigned as the name for the element. Hence we use the [formGroupName]="i" where i is the index of the FormArray to bind the FormGroup to the div element. Finally, we add the controls using the formControlName directive.
Whereas FormGroup represents an entire form or a fixed subset of a form's fields, FormArray usually represents a collection of form controls that can grow or shrink. For example, you could use FormArray to allow users to enter an arbitrary number of emails.
The key here is to navigate your form structure. Once you get to the form group that _id is in, you can use removeControl('_id') to remove the control.
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);
}
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With