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.
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.
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);
}
Updated plunker
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