Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular model and rendering out of sync

Tags:

angular

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.

  1. Add 3 new objects to the array by clicking the "Add" button
  2. Populate the input fields with some data
  3. Click the Remove button on the middle item to remove it
  4. Add 1 new object by clicking the "Add" button

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;
}
like image 949
Tosho Toshev Avatar asked Sep 12 '17 13:09

Tosho Toshev


1 Answers

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;
}
like image 54
Faly Avatar answered Oct 21 '22 11:10

Faly