I have an Angular component that has a button which adds a new object to an array that serves as a model. In my component template, I have an *ngFor loop which iterates through the objects in the array and displays input fields for the properties of the object. I also have a "remove" button next to each object that removes the object from the array. When I do the following steps the UI goes out of sync with the model and empties the fields of all items except the first one.
What is making the UI go out of sync with the model?
Here is a Plunker example that demonstrates the issue Example I also added a line in the template that shows what is in the model array.
@Component({
selector: 'my-app',
template: `
<div>
<button (click)="things.push({})">+ Add new thing</button>
<br />
<br />
<form #contactForm="ngForm">
<ng-container *ngFor="let thing of things;let i = index">
<input [(ngModel)]="thing.name" name="name-{{i}}" id="name-{{i}}" placeholder="name"/>
<br />
<input [(ngModel)]="thing.otherstuff" name="other-{{i}}" id="other-{{i}}" placeholder="other" />
<button (click)="things.splice(i, 1)">Remove</button>
<br />
<br />
</ng-container>
</form>
{{things | json}}
</div>
`,
})
export class App {
things: Awesome[]
constructor(){
this.things = new Array();
}
}
@NgModule({
imports: [
BrowserModule,
FormsModule
],
declarations: [ App ],
bootstrap: [ App ]
})
export class AppModule {
}
export class Awesome{
name?: string;
otherstuff?: string;
}
Don't use index i to give value for name attribute. i is not stable when splicing items from your array so you have to generate an unique id for each new added thing (I provided an exemple function that generate unique id).
The code below works:
//our root app component
import { Component, NgModule, VERSION } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { FormsModule } from "@angular/forms";
@Component({
selector: 'my-app',
template: `
<div>
<button (click)="addEmptyItem()">+ Add new thing</button>
<br />
<br />
<form>
<ng-container *ngFor="let thing of things">
<input [(ngModel)]="thing.name" name="name-{{thing.id}}" id="name-{{thing.id}}" placeholder="name"/>
<br />
<input [(ngModel)]="thing.otherstuff" name="other-{{thing.id}}" id="other-{{thing.id}}" placeholder="other" />
<button (click)="removeItem(thing)">Remove</button>
<br />
<br />
</ng-container>
</form>
{{things | json}}
</div>
`,
})
export class App {
things: Awesome[]
constructor() {
this.things = new Array();
}
removeItem(thing): void {
this.things = this.things.filter(th => th.name !== thing.name);
}
addEmptyItem(): void {
let newItem = new Awesome();
newItem.id = this.guid();
this.things.push(newItem);
}
private guid() {
let uniqueId = Math.random().toString(36).substring(2)
+ (new Date()).getTime().toString(36);
return uniqueId;
}
}
@NgModule({
imports: [
BrowserModule,
FormsModule
],
declarations: [App],
bootstrap: [App]
})
export class AppModule {
}
export class Awesome {
id?: string;
name?: string;
otherstuff?: string;
}
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