I am creating a form that consists in two FormArray
s, items
and savedItems
.
On each element in the form array there is a button that can remove that element from the array.
The top/first FormArray
is populated from a list of objects. While the second FormArray
is for the user to fill.
The problem is, on the first FormArray
whenever I click on the "Remove Entry" button all elements in the FormArray
are removed instead of the element belonging to (in the same position) the "Remove Entry" button.
Here is my form definition:
<form [formGroup]="boxForm" (ngSubmit)="onSubmit(boxForm)">
<div class="row" formArrayName="savedItems" *ngFor="let item of getSavedItems.controls; let i = index;" >
<div *ngFor="let trailer of trailerModelList; let x = index;" >
<div class="form-group" [formGroupName]="i" style="margin-bottom: 10px">
<div>
<div class="col-sm-5 form-group">
<label for="name">Origin</label>
<input class="form-control" type="text" placeholder="From" value="{{trailer.fromLocation}}"/>
</div>
<div class="col-sm-5 form-group">
<label for="name">Destination</label>
<input class="form-control" type="text" placeholder="To" value="{{trailer.toLocation}}" />
</div>
<div class="col-sm-3 form-group">
<label for="name">Name</label>
<input class="form-control" type="text" placeholder="Name" value="{{trailer.name}}"/>
</div>
<div class="col-xs-2">
<button class="btn btn-danger" type="button" (click)="removeSavedItem(i)">Remove Entry</button>
</div>
</div>
</div>
</div>
</div>
<hr>
<div class="row" formArrayName="items" *ngFor="let item of getItems.controls; let i = index;" >
<div class="form-group" [formGroupName]="i">
<div class="col-md-5 form-group" >
<label for="name">Origin</label>
<input class="form-control" type="text" formControlName="origin" >
</div>
<div class="col-md-5 form-group">
<label for="name">Destination</label>
<input class="form-control" type="text" formControlName="to">
</div>
<div class="col-sm-3 form-group">
<label for="name">Name</label>
<input class="form-control" type="text" formControlName="name" placeholder="Name"/>
</div>
<div class="col-xs-2">
<button class="btn btn-danger" type="button" (click)="removeItem(i)">Remove Entry</button>
</div>
</div>
</div>
<button class="btn btn-success" type="submit" style="margin-right: 10px">Go</button>
<button class="btn btn-primary" type="button" (click)="addItem()" style="margin-right: 10px">New Box</button>
And here is how I create, add and remove items for the FormArray
:
get getItems(): FormArray {
return this.boxForm.get('items') as FormArray;
}
get getSavedItems(): FormArray {
return this.boxForm.get('savedItems') as FormArray;
}
createBox(): FormGroup {
return this.formBuilder.group({
name: ['', [Validators.required, Validators.minLength(3)]],
origin: ['', [Validators.required, Validators.minLength(1)]],
to: ['', [Validators.required, Validators.minLength(1)]]
});
}
addItem(): void {
this.getItems.push(this.createBox());
}
removeItem(index) {
this.getItems.removeAt(index);
}
removeSavedItem(index) {
this.getSavedItems.removeAt(index);
}
Here is a stackblitz with a reproducible code of my issue. If you click on any "Remove Entry" above the horizontal bar both entries will be removed from that element in the FormArray
instead of only the selected one.
Why is this happening? I am quite sure it is related to having two for loops. One for the savedItem controls and an inner loop to loop over the elements in my list to populate the FormArray
but I cannot pinpoint the issue, I am still learning my way with Angular
The problem is in the creating your FormArray
-property savedItems
, also this line:
...
savedItems: this.formBuilder.array([this.createBox()])
...
This lines means, that you define only one FormGroup
in your FormArray
and so you have one element with index = 0
.
In your template file you in block for savedItems
you have two loops:
1.) *ngFor="let item of getSavedItems.controls; let i = index;"
This is right loop and will give your right functionality for removeAt
.
2.) *ngFor="let trailer of trailerModelList; let x = index;"
This is wrong loop, because you can not create FromArray
-items from template, so as you have now.
Problem in short: By clicking on Remove entry
-button both(all) savedItems
will be removed from array, because you have only one element with index = 0 in FormArray
.
Solution:
Step 1: Create for each trailerModelList
-item an FormArray
with createBox()
-method.
ngOnInit() {
this.boxForm = this.formBuilder.group({
items: this.formBuilder.array([this.createBox()]),
savedItems: this.formBuilder.array([])
});
this.trailerModelList = new Array();
this.trailerModelList.push(new TrailerModel(1, 'test', 'London', 'Paris'));
this.trailerModelList.push(new TrailerModel(2, 'test2', 'Amsterdam', 'Berlin'));
this.trailerModelList.forEach(item => {
(this.boxForm.get('savedItems') as FormArray).controls.push(this.createBox(item.name, item.fromLocation, item.toLocation));
});
}
....
createBox(name: string = null, origin: string = null, to: string = null): FormGroup {
return this.formBuilder.group({
name: [name, [Validators.required, Validators.minLength(3)]],
origin: [origin, [Validators.required, Validators.minLength(1)]],
to: [to, [Validators.required, Validators.minLength(1)]]
});
}
Step 2: Remove following loop from template: *ngFor="let trailer of trailerModelList; let x = index;"
Step 3: Add formControlName
-property to your form items inside this loop: *ngFor="let item of getSavedItems.controls; let i = index;"
<div class="row" formArrayName="savedItems" *ngFor="let item of getSavedItems.controls; let i = index;" >
<div>
<div class="form-group" [formGroupName]="i" style="margin-bottom: 10px">
<div>
<div class="col-sm-5 form-group">
<label for="name">Origin</label>
<input class="form-control" type="text" placeholder="From" formControlName="origin"/>
</div>
<div class="col-sm-5 form-group">
<label for="name">Destination</label>
<input class="form-control" type="text" placeholder="To" formControlName="to" />
</div>
<div class="col-sm-3 form-group">
<label for="name">Name</label>
<input class="form-control" type="text" placeholder="Name" formControlName="name"/>
</div>
<div class="col-xs-2">
<button class="btn btn-danger" type="button" (click)="removeSavedItem(i)">Remove Entry</button>
</div>
</div>
</div>
</div>
</div>
Here is stackblitz with working solution.