Search code examples
javascriptarraysangularcontenteditable

Contenteditable DOM updates incorrectly when array changes. Angular


I've created a Plunker.

The code is here:

@Component({
  selector: 'my-app',
  template: `
    <div>
      <h2>Hello {{name}}</h2>
      <button (click)="changeArrayToOne()">ChangeOne</button>
      <button (click)="changeArrayToTwo()">ChangeTwo</button>
      <table>
        <tr>
            <td *ngFor="let c0 of array" contenteditable='true'>
                {{c0}}
            </td>
        </tr>
      </table>
    </div>
  `,
})
 export class App {
  name:string;
  initial = [];
  one = [];
  two = [];
  array = [];
  constructor() {
    this.initial = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
    this.one = [4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0];
    this.two = [4,4,4,5,0,0,0,0,0,0,0,0,0,0,0,0];
    this.array = this.initial.slice();
  }

  changeArrayToOne() {
    this.array = this.one.slice();
  }

  changeArrayToTwo() {
    this.array = this.two.slice();
  }
}

`

I'm trying to create editable table that is populated by array with ngFor. I use contenteditable='true' attribute for that.

Table is being populated with this.array variable. I also have two buttons ChangeOne and ChangeTwo, which replace all values of this.array with values from arrays this.one and this.two.

So, here are steps to reproduce the problem:

  • Run the plunker (table is populated with values of this.initial array).
  • The first element in the table row is 0. Change it to 10 (remember, table is editable).
  • Press the button ChangeOne. It will replace this.array values from this.initial to this.one.

As you can see, the first three elements were replaced with 4's and the 10 has moved to the fourth position. If you'll press ChangeTwo or ChangeOne again, the 10 will just move to the right.

Why?! And how can it be fixed? I don't want 10 to be present at all after I replace the array.


Solution

  • Here is a Plunker to show how it works. The code to use is this :

    customTrackBy(index, item) {
      return index + '-' + item;
    }
    

    HTML :

    *ngFor="let c0 of array; trackBy: customTrackBy"
    

    Because you array is made of primtive values, you have to tell Angular how to check for changes. Since the value changes, you have to return the item, and if you have an issue with the index, you have to return the index too.

    See this as a custom way of creating IDs for your array.