Angular process of detecting changes in repainting

I am researching the angular change detection process and checking the Chrome developer tools, I see strange behavior.

My plnkr for demo behavior: http://plnkr.co/edit/cTLF00nQdhVmkHYc8IOu

I have a simple component view:

<li *ngFor="let item of list">{{item.name}}</li>

      

and its constructor:

constructor() {
    this.list = [{name: 'Gustavo'}, {name: 'Costa'}]

      

to simulate a simple request, I added:

// simulating request repaint the DOM
setInterval( () => {
  this.list = [{name: 'Gustavo'}, {name: 'Costa'}];
}, 2000);

      

If you notice, list

arrays gets a list equal to the initial value. Let's imagine that when Angular checks the values ​​during change detection, we have code like this:

if( oldName !== name )  { // ( 'Gustavo' !== 'Gustavo')
 // update the view
}

      

But the values ​​are the same, why angular REPAINT THE DOM every 2 seconds.?

But if I mutate the object, REPAINT doesn't happen

// simulating request there is not repaint
setInterval( () => {
  this.list[0].name = "Gustavo"; // no repaint because it the same value
  this.list[1].name = "Costa 2"; // repaint
}, 2000);

      

You can check this with the plnkr link above.

+4


source to share


3 answers


This is due to what Angular uses trackByFunction

by default trackByFunction

to track items by ID.

const trackByIdentity = (index: number, item: any) => item;

      

So when you create a new array, it creates new object references and Angular detects the change. Even if you haven't changed the reference to the array, Angular will still think that the elements have changed because the object references change:

setInterval( () => {
  this.list.length = 0;
  this.list.push({name: 'Gustavo'});
  this.list.push({name: 'Costa'});
}, 2000);

      



You can provide custom trackByFunction

tracking by object name:

@Component({
  selector: 'my-app',
  template: `
   <li *ngFor="let item of list; trackBy:identify">{{item.name}}</li>
  `
})
export class App {
  list:[];

  identify(index, item){
     return item.name; 
  }

      

This way the DOM won't update. See this plunker .

As you are interested ngFor

, you can also read this answer where I explain how it ngFor

works under the hood.

+5


source


This is because every time a new array is created and angular is updated because the link has changed. If you assign it to the same link every 2 seconds, it won't.

otherList = [{name: 'Gustavo'}, {name: 'Costa'}];

constructor() {
  this.list = [{name: 'Gustavo'}, {name: 'Costa'}]
  setInterval( () => {
    this.list = this.otherList;
  }, 2000);    
}

      



Updated plunker

+1


source


To add to @ 0mpurdy, Objects

(etc. Arrays

) will never be equal, even if they have the same properties and values, unless they are a reference to another, or if they both have the same reference.

Primitives, on the other hand, can be equal to other primitives because they are compared by value, not by reference. So change detection doesn't happen when you overwrite values ​​manually for the same value, but if you replace the whole thing, even if the objects appear to be equal, change detection changes.

+1


source







All Articles