Reputation: 10777
I have DTO in my code as follow :
export class MatrixDTO implements cols {
constructor() {
this.name = "";
this.items = [];
this.customRow = false;
this.label="";
this.customCol=false;
}
name : string;
items :Array<cols>;
customRow:boolean;
label: string;
customCol:boolean;
}
interface cols {
label: string;
customCol:boolean;
}
Forms init code as follow :
initilazieForm(): void {
this.matrixForm = this.fb.group({
matrix: this.fb.array([], [Validators.required]),
});
}
How should I declare my form array matrix so I can add new rows and columns also and same reflect when I save form.
Edited as per comments :
I am looking for a final FormArray to be something like this and where customRow or customCol is false , I want to show label instead of text boxes.
[
{
"label": "Apple",
"customRow": false,
"values": [
{
"label": "30",
"customCol": false
},
{
"label": "30",
"customCol": false
},
{
"label": "30",
"customCol": false
}
]
},
{
"label": "Bannana",
"customRow": false,
"values": [
{
"label": "50",
"customCol": false
},
{
"label": "60",
"customCol": false
},
{
"label": "70",
"customCol": false
}
]
},
{
"label": null,
"customRow": true,
"values": [
{
"label": "",
"customCol": true
},
{
"label": "",
"customCol": true
},
{
"label": "",
"customCol": true
}
]
}
]
Edit for patch value error :
#1 JSON I am getting through service while come on same form for edit : you can check only matrix node JSON ,rest is related to other controls related values.
{
"name":"rahul",
"brand":"Brand",
"market":"Market",
"productName":"yVW4LHi1_Uazy2_aKVfaSw",
"isArchived":false,
"isCustom":false,
"matrix":[
{
"label":"Apple",
"customRow":false,
"items":[
{
"label":"10",
"customCol":false
},
{
"label":"20",
"customCol":false
},
{
"label":"30",
"customCol":false
},
{
"label":"40",
"customCol":true
}
]
},
{
"label":"Bannana",
"customRow":false,
"items":[
{
"label":"12",
"customCol":false
},
{
"label":"22",
"customCol":false
},
{
"label":"32",
"customCol":false
},
{
"label":"22",
"customCol":true
}
]
},
{
"label":"Test",
"customRow":true,
"items":[
{
"label":"1",
"customCol":true
},
{
"label":"2",
"customCol":true
},
{
"label":"3",
"customCol":true
},
{
"label":"4",
"customCol":true
}
]
}
]
}
#2 and code I am trying to use to patch values is as follow :
setFormValues(data: any) {
console.log(data);
this.specForm.patchValue({
name: data.name,
Brand: data.Brand,
Market : data.Market,
productName : '',
IsArchived : data.IsArchived,
OriginId : data.OriginId,
IsCustom : data.IsCustom,
specs: this.fb.array([], [Validators.required]),
matrix: data.matrix.map((x:any) => this.rowGroup(x))
});
}
Upvotes: 0
Views: 2626
Reputation: 58039
I think that it's better think first in the object you need.
I imagine you need an object like
data=[
{label:"Apple",values:[10,20,30]},
{label:"Bannana",values:[12,22,32]}
];
It's an array of object with two properties, "label" and "values", values are an array on numbers.
So You need a FormArray of FormGroup with a FormControl and a FormArray
If I imagine you has an object with label and values I can make a function
rowGroup(data:any=null){
return new FormGroup({
label:new FormControl(data.label),
values:new FormArray(data.values.map(x=>new FormControl(x)))
})
}
So, our formArray is simple
formArray = new FormArray(
this.data.map(x => this.rowGroup(x))
);
As I want to mannage the formArray directly we need a function that return the formGroup inside
group(index)
{
return this.formArray.at(index) as FormGroup
}
As we need manage the formArray "values" we use another function
valuesArray(index)
{
return this.group(index).get('values') as FormArray
}
Now we can manage the formArray using an html like
<div *ngFor="let sub of formArray.controls;let i=index">
<div [formGroup]="group(i)">
<input formControlName="label">
<ng-container formArrayName="values">
<ng-container *ngFor="let group of valuesArray(i).controls;let j=index">
<input [formControlName]="j">
</ng-container>
</ng-container>
</div>
</div>
To add a new row, the only is push a new formGroup in our formArray. To create the formGroup we use the function rowGroup passing a "data" create a doc
addRow()
{
//we create an array of "values" with so many elements the first "values"
//or with 3 elements
const values=this.formArray.length && this.valuesArray(0)?
this.valuesArray(0).value.map(x=>null):[null,null,null]
const empty={label:null,values:values}
this.formArray.push(this.rowGroup(empty))
}
To add a column, we loop over the formArray.controls and add a FormControl to the "values" array. We need "cast" to FormArray and FormGroup, but it's not very complex
addColumn()
{
this.formArray.controls.forEach(x=>{
((x as FormGroup).get('values') as FormArray).push(new FormControl(null))
})
}
The stackblitz
Update to manage the json object propused we need create a new funciton
itemsGroup(data:any=null)
{
data=data || {label:null,customCol:true}
return new FormGroup({
label:new FormControl(data.label),
customCol:new FormControl(data.customCol),
})
}
That allow us crreate the group
see a new stackblitz
Update 2 as formArray is belong to a FormGroup we are going to change a few the name of the functions. And create a getter of the formArray, so we has
//this is our old "formArray"
get matrixFormArray() {
return this.specForm ? (this.specForm.get('matrix') as FormArray) : null;
}
//the FormGroup this is our old "group(index)"
matrixElement(index) {
return this.matrixFormArray.at(index) as FormGroup;
}
/*Function to mannage the formArray "items"*/
itemsFormArray(index) {
return this.matrixElement(index).get('items') as FormArray;
}
To give value to a FormGroup we can take two aproach, use pathValue or directly create the form with the values. If we use pathValue, when we define the form we'll use some like
specForm=new FormGroup({
name:new FormControl(),
Brand:new FormControl(),
Market:new FormControl(),
....
matrix:new FormArray([]) //<--see in this case you defined the "matrix"
// as "empty" fromArray
})
Is we are going to create the form with the data we can simply use
specForm: FormGroup=null; //<--our FormGroup. I choose at first declare as null
But in this case is important to avoid initials error use a *ngIf in our form like
<form *ngIf="specForm" [formGroup]="specForm">
....
</form>
the new stackblitz with the changes
Upvotes: 1
Reputation: 144
you have to push formgroups into your formarray, and each object property is a formcontrol
createForm() {
this.matrixForm = this.fb.group({
matrix: this.fb.array((this.matArray).map(mat => {
this.fb.group({
name: mat.name
...
})
}), [Validators.required]),
});
}
and add new rows like this
addMat(mat){
this.matrixForm.get('matrix').push(this.fb.group({
name: mat.name
.....
}))
}
Upvotes: 1