The component model:
private SomeArray = [{ key: "Initial" }];
User can add/remove items dynamically:
addField() {
this.SomeArray.push({ key: Math.random().toString() });
}
removeField(index: number) {
this.SomeArray.splice(index, 1);
}
Template markup:
<div class="col-xs-12">
<button (click)="addField()" type="button">Add</button>
</div>
<div *ngFor="let field of SomeArray; let i = index;">
<input [(ngModel)]="field.key" #modelField="ngModel" [name]=" 'SomeArray['+i+'].key' " type="text" class="form-control" required />
<div [hidden]="modelField.pristine || !(modelField.errors && modelField.errors.required)" class="alert alert-danger">
Required error
</div>
<button (click)="removeField(i)" class="btn btn-danger">Remove</button>
</div>
This works untill user removes any item from SomeArray. If I add some two items initially:

and remove the one with 1 index:

then after adding another item Angular treat it as item has both 0 and 1 index (the new item "occupies" both two inputs):

(item with key 0.1345... is not displayed)
It's worth to noting items of SomeArray are as expected, but data binding fails. What can be the reason of it?
Update: Thanks to the comments of @Stefan Svrkota and @AJT_82 it's known for me the issue can be resolved by adding [ngModelOptions]="{standalone: true}" to the needed input. But I couldn't stop thinking about the reason of the issue in my cause, without setting standalone option (there is unique value for each name attribute so it's excepted nothing wrong here).
Finally I have found that behavior occurs when input elements are into <form> tag only - Plunker sample here (enclosing of template with form tag is the reason that issue).
Any ideas of this behavior?
The reason why it happens is ngFor mixes name properties when you delete some item.
When you use ngModel inside form each ngModel control will be added to form controls collection.
Let's see what happens if we have added three items and clicked on Remove the second
1) Step1 - SomeArray[1].key exists in collection controls

2) Step2 - SomeArray[1].key has been removed from controls collection

3) Step3 - Html looks like

4) Step4 We are adding a new item

So formGroup returns existing item.
How we can solve it?
1) Don't wrap our controls in form tag
2) Add ngNoForm attribute to form
<form ngNoForm>
3) Use
[ngModelOptions]="{standalone: true}
With all three solutions above:
We can remove [name] property binding
We can't use the built in Form group validation
4) Use trackBy for ngFor
template.html
<div *ngFor="let field of SomeArray; let i = index; trackBy: trackByFn">
component.ts
trackByFn(i: number) {
return i;
}
Plunker Example
This way our built in form will work properly
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With