Issue
I'm trying to dynamically control the number of FormGroups with a FormArray such that I set the FormArray length.
myForm = this.fb.group({
formGroupsToAdd: ['5'],
formList: this.fb.array(this.getFormControls(5))
});
getFormControls(numberOfFormGroups: number) {
const myArray = new Array(numberOfFormGroups);
myArray.fill(this.fb.group({
length: [''],
width: [''],
height: ['']
}));
return myArray;
}
I also have a way of getting this formList: FormArray
with
get unitList() {
return this.myForm.get('formList') as FormArray;
}
However, when I go to add a value to a single control in this FormArray
, it updates all FormGroup
s in the FormArray
.
<div formArrayName="formList">
<div *ngFor="let unitForm of formList.controls; let i = index">
<div [formGroup]="formList.controls[i]">
<pre>{{formList.controls[i].value | json}}</pre>
<pre>i = {{i}}</pre>
<mat-form-field>
<input matInput formControlName="length" />
</mat-form-field>
<mat-form-field>
<input matInput formControlName="width" />
</mat-form-field>
<mat-form-field>
<input matInput formControlName="height" />
</mat-form-field>
</div>
</div>
</div>
So my formList.value
looks like this...
[
{
"length": "",
"width": "asdf",
"height": ""
},
{
"length": "",
"width": "asdf",
"height": ""
},
{
"length": "",
"width": "asdf",
"height": ""
},
{
"length": "",
"width": "asdf",
"height": ""
},
{
"length": "",
"width": "asdf",
"height": ""
}
]
Where instead I need a single FormGroup
to contain of course it's own unique FormControl
values.
What am I doing wrong? Why doesn't the [formGroup]="formList.controls[i]"
satisfy my need here by providing a unique identifier for each formGroup?
EDIT: I've added a stackblitz with my problem.
https://stackblitz.com/edit/angular-ivy-sa7pbb?file=src/app/app.component.html
Solution
You should use [formGroupName]="idx" like this.
<div formArrayName="formList">
<div *ngFor="let unitForm of formList.controls; let i = index" [formGroupName]="i">
<mat-form-field>
<input matInput formControlName="length" />
</mat-form-field>
<mat-form-field>
<input matInput formControlName="width" />
</mat-form-field>
<mat-form-field>
<input matInput formControlName="height" />
</mat-form-field>
</div>
</div>
And some useful functions:
// Call init form on init
ngOnInit(): void {
this.initForm();
}
// init form here
initForm() {
this.myForm = this.fb.group({
formList: this.fb.array([])
});
// Call this 5 times or any times you want to init rows.
this.addItem();
}
// Add new item to make an addition button on UI
addItem() {
// You can enter input into newItem function to load data from API to make update/details screen.
this.unitList.push(this.newItem(0, 0, 0));
}
// Your function to get fromList
unitList() {
return this.myForm.get('formList') as FormArray;
}
// new Item as a FormGroup to add into main form and it will auto render on UI
newItem(length: number, width: number, height: number): FormGroup {
const newGroup = this.fb.group({
length: [length],
width: [width],
height: [height]
})
return newGroup;
}
// Clear all items to make a refresh/clean UI
clearAllList() {
this.unitList.clear();
}
// Remove item at index to make a delete button
removeItem(index: number) {
this.unitList.removeAt(idx);
}
Updated: Fix issue on stackblitz: https://stackblitz.com/edit/angular-ivy-rtmpwc?file=src/app/app.component.ts
The issue is myArray.fill
, and when you use it, it will use 1 reference variable to set for all 5 items in your array. So when you change one item, it will ref into 5 items
getFormControls(numberOfFormGroups) {
const myArray = new Array(numberOfFormGroups);
myArray.fill(this.fb.group({
length: [''],
width: [''],
height: ['']
}));
return myArray;
}
So the fixed function should be like this. It will create 5 reference for 5 items.
getFormControls(numberOfFormGroups) {
const myArray = new Array();
for (let i = 1; i <= numberOfFormGroups; i++) {
let newItem = this.fb.group({ length: [''], width: [''], height: [''] });
myArray.push(newItem);
}
return myArray;
}
Answered By - Tartarus
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.