row:1
, col:0
.I tried various ways to define FormArray
holding an array of FormControl
s. But I am unsure how to access the respective FormControl
by row and column indices alone in the Angular template.
Model
[
['a', 'b'],
['c', 'd']
]
FormGroup
form = new FormGroup({
rows: new FormArray([...])
});
Expected result
I am tried various things similar to this:
<form [formGroup]="form"">
<div formArrayName="rows">
<div
*ngFor="let row of rows.controls; let rowIndex = index" [formGroupName]="rowIndex">
<div formArrayName="cols">
<div
*ngFor="let col of form.get('cols').controls; let colIndex = index"
[formGroupName]="colIndex">
<input [formControlName]="colIndex" />
</div>
</div>
</div>
</div>
</form>
Dabbel, if you has an Array of Array, create a FormArrays of FormArrays (sorry for the joke)
Well, imagine you has data=[ ['a', 'b'], ['c', 'd'] ]
You can in ngOnInit create the formArray of FormArray like
//At firs a empty FormArray
this.formArray = new FormArray([]);
//with each element of data
this.data.forEach(x => {
//x is e.g.['a','b']
//we create a emptt FormArray
const obj = new FormArray([]);
//add a FormControl
x.forEach(y => {
obj.push(new FormControl(y));
});
//and push in the formArray
this.formArray.push(obj);
});
or abreviated using map like
this.formArray=new FormArray(
this.data.map(x=>new FormArray(
x.map(y=>new FormControl(y))))
)
Well, How mannage a FormArray outside a FormGroup? If our FormArray is a FormArray of FormGroup, we make in general
<!--yes we can use [formGroup] with a FormArray-->
<form [formGroup]="formArray">
<!--iterate over the formArray.controls, that is a formGroup-->
<div *ngFor="let group of formArray.controls;let i=index">
<div [formGroup]="group">
<input formControlName="name">
...
</div>
</div>
</form>
Well, our formArray is a FormArray of FormArray, but remember that we using [formGroup] with an array and iterate over formArray.controls.
<form [formGroup]="formArray">
<div *ngFor="let subarray of formArray.controls;let i=index">
<div [formGroup]="subarray">
<ng-container *ngFor="let control of subarray.controls;let j=index">
<input [formControl]="control">{{control.invalid?"*":""}}
</ng-container>
</div>
</div>
</form>
NOTE: I use <ng-container>
nor <div>
to not create addicionals divs. (We can not put the *ngFor in the own input because, then, we can not have access to control.invalid
Well, as you want create Validators, we are changing a bit when we create the formGroup to include the validators, I put a "fool" example
this.formArray=new FormArray(
this.data.map(x=>new FormArray(
x.map(y=>new FormControl(y,Validators.required)),
this.rowValidator())),this.arrayValidator()
)
And ours validators can be like
rowValidator()
{
return (array:FormArray)=> {
const invalid:boolean=array.value.
filter((x,index)=>array.value.indexOf(x)!=index).length>0
return invalid?{error:'must be different'}:null
}
}
arrayValidator()
{
return (array:FormArray)=> {
let arrayJoin="";
array.value.forEach(x=>arrayJoin+=x.join(''))
return arrayJoin=="abcd"?null:{error:'must be a,b,c,d'}
}
}
You can see in the stackblitz
NOTE: In a real application, we neen't use so many Validators. Take account the cost of this validator on the perfomance of the app