when we work with complex FormsArrays it's better
1.-To have functions "get" that return the formArrays, see that the "XsArray" can be a getter, the others need be a normal function because need an index. So
get XsArray() {
return this.form.get("Xs") as FormArray;
}
getYsArray(index) {
return this.XsArray.at(index).get("Ys") as FormArray;
}
getWsArray(index) {
return this.XsArray.at(index).get("Ws") as FormArray;
}
getZsArray(index, indexz) {
return this.getYsArray(index).at(indexz).get("Zs") as FormArray;
}
See that you can use the function XsArray in the function getWsArray, e.g.
2.-The other thing is to have functions that return formGroup. This functions are feed with an object or with null. If is feed with null we add an object by default. Here are:
setXs(el:any=null)
{
el=el||{X:null}
return this.fb.group({
X:[el.X,Validators.required],
Ys:el.Ys?this.fb.array(el.Ys.map(x=>this.setYs(x))):this.fb.array([]),
Ws:el.Ws?this.fb.array(el.Ws.map(x=>this.setWs(x))):this.fb.array([])
})
}
setYs(el:any=null)
{
el=el || {product:null}
return this.fb.group({
product:[el.product, [Validators.required]],
Zs:el.Zs?this.fb.array(el.Zs.map(x=>this.setZs(x))):this.fb.array([])
})
}
setWs(el:any=null){
el=el || {state:null,city:null}
return this.fb.group({
state: [el.state, [Validators.required]],
city: [el.city, [Validators.required]]
})
}
setZs(el:any=null)
{
el=el || {Z:null}
return this.fb.group({
Z:[el.Z, [Validators.required, Validators.pattern("[0-9]{3}")]]
})
}
Hey! wait a moment what on earth mean this strange: this.fb.array(el.Zs.map(x=>this.setZs(x))). Well, el.Zs is an array, we transform each element of the array in a FormGroup (remember that this.setZs(x) return a formGroup) and with the array of formGroup we create the formArray. I know that sound complex we coud make in severals steps
//this.fb.array(el.Zs.map(x=>this.setZs(x)))
//is the same that
let arr=this.fb.array([])
el.Zs.forEach(x=>{
arr.push(this.setZs(x)
})
Well, now it's all more comfortable. See how the functions to add new elements becomes so easy like
addX() {
this.XsArray.push(this.setXs());
}
addY(ix) {
this.getYsArray(ix).push(this.setYs());
}
addZ(ix, iy) {
this.getZsArray(ix,iy).push(this.setZs());
}
addW(ix) {
this.getYsArray(ix).push(this.setWs())
}
An to remove so simple as, e.g.
removeW(ix, iw) {
this.getWsArray(ix).removeAt(iw)
}
Well, At least, in ngOnInit we use the seedData like
this.form = this.fb.group({
Xs: this.fb.array(this.seedData.Xs.map(x=>this.setXs(x)))
});
When we show in .html we use also the functions before explained iterating over the formArray.controls
<form [formGroup]="form">
<div formArrayName="Xs">
<div *ngFor="let X of XsArray.controls; let ix=index">
<div [formGroupName]="ix" class="Xs">
<input type="text" formControlName="X">
...
<div formArrayName="Ys">
<div *ngFor="let Y of getYsArray(ix).controls; let iy=index">
<div [formGroupName]="iy" class="Ys">
...
<div formArrayName="Zs">
<div *ngFor="let Z of getZsArray(ix,iy).controls; let iz=index">
<div [formGroupName]="iz" class="Zs">
....
</div>
</div>
...
</div>
</div>
</div>
...
</div>
<div formArrayName="Ws">
<div *ngFor="let W of getWsArray(ix).controls; let iw=index">
<div [formGroupName]="iw" class="Ws">
....
</div>
</div>
<input type="button" (click)="addW(ix)" value="Add W">
</div>
</div>
</div>
...
</div>
<form>
Well, the stackblitz is here
NOTE: A FormArray can be a FormArray of FormGroup or a FormArray of FormControls, if only has a unique property (e.g. the FormArray "Zs"), generally you should use a FormArray of FormControls