Reputation: 129
I'm learning about Angular change detection process and I'm checking the Chrome dev tools, I see strange behavior.
My plnkr to demonstrate the behavior: http://plnkr.co/edit/cTLF00nQdhVmkHYc8IOu
I have a simple component view:
<li *ngFor="let item of list">{{item.name}}</li>
and the its constructor:
constructor() {
this.list = [{name: 'Gustavo'}, {name: 'Costa'}]
to simulate a simple request I've added:
// simulating request repaint the DOM
setInterval( () => {
this.list = [{name: 'Gustavo'}, {name: 'Costa'}];
}, 2000);
If you noticed, the array list
receives a list equal to the initial value. Let's imagine when Angular checks the values in view in change detection process we have a 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, the REPAINT does not occur
// simulating request there is not repaint
setInterval( () => {
this.list[0].name = "Gustavo"; // no repaint because it's the same value
this.list[1].name = "Costa 2"; // repaint
}, 2000);
You can test this with the plnkr link above.
Upvotes: 4
Views: 2234
Reputation: 105545
This is because Angular uses default trackByFunction
for the DefaultIterableDiffer
that tracks items by identity.
const trackByIdentity = (index: number, item: any) => item;
So obviously when you create a new array it creates new object references and Angular detects changes. Even if you didn't change array reference, Angular will still think that items are changed because object references change:
setInterval( () => {
this.list.length = 0;
this.list.push({name: 'Gustavo'});
this.list.push({name: 'Costa'});
}, 2000);
You can provide you custom trackByFunction
to track 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;
}
In this way the DOM will not be updated. See this plunker.
Since you are curios about ngFor
you can also read this answer where I explain how ngFor
works under the hood.
Upvotes: 6
Reputation: 1467
To add to @0mpurdy, Objects
(and so do Arrays
) are never equal even if they have the same properties and values, unless one is a reference of another or if they both share the same reference.
Primitives, on the other hand, can be equal to other Primitives cause they're compared by value, not by reference. That's why there's no change detection triggered when you overwrite the values manually for the same value, but if you replace the whole thing, even if the objects are apparently equal, change detection is going to get triggered.
Upvotes: 1
Reputation: 3353
This is because you are creating a new array every time and angular is updating because the reference has changed. If you assign it to the same reference every 2 seconds this will not be the case
otherList = [{name: 'Gustavo'}, {name: 'Costa'}];
constructor() {
this.list = [{name: 'Gustavo'}, {name: 'Costa'}]
setInterval( () => {
this.list = this.otherList;
}, 2000);
}
Upvotes: 1