When you're working with an array of primitive values (like numbers or strings), Angular's ngFor
directive works pretty straightforwardly. It keeps track of each item's position in the array. So if the array changes – items get added, removed, or updated – Angular knows exactly which parts of the list need updating because it's based on their position.
However, when you're using an array of objects (as in your second example), Angular needs more information to track changes efficiently. By default, Angular tracks objects in the array by their identity. When you have an array of objects bound to a template using *ngFor
, any change to the array triggers Angular's change detection mechanism to update the corresponding DOM elements. However, Angular tries to optimize this process by comparing the objects in the array by reference.
When you add an object in the middle of the array, Angular's default change detection algorithm doesn't have a way to know where exactly the change occurred. It simply sees that the array reference has changed, so it assumes that every item in the array might have changed and updates all corresponding DOM elements.
By using a trackBy
function, Angular can more efficiently determine which items have been added, removed, or moved within the array, and only update the corresponding DOM elements for those specific items. This can significantly reduce the amount of DOM manipulation required and improve the overall performance of your application.
Add the below code in your angular project. Then in the browser inspect and see the how dom updates it.
HTML:
<div>
<button (click)="refresh()">Refresh</button>
<ul>
<li *ngFor="let user of users">{{ user.name }}</li>
</ul>
</div>
TS:
users = [
{ id: 1, name: 'Steve' },
{ id: 2, name: 'John' },
{ id: 3, name: 'Carol' }
];
refresh() {
this.users = [
{ id: 1, name: 'Steve' },
{ id: 2, name: 'John' },
{ id: 3, name: 'Sarah' },
{ id: 4, name: 'Carol' },
{ id: 5, name: 'Mike' }
];
}
HTML:
<div>
<button (click)="refresh()">Refresh</button>
<ul>
<li *ngFor="let user of users; trackBy: trackById">{{ user.name }}</li>
</ul>
</div>
TS:
users: { id: number, name: string } = [
{ id: 1, name: 'Steve' },
{ id: 2, name: 'John' },
{ id: 3, name: 'Carol' }
];
refresh() {
this.users = [
{ id: 1, name: 'Steve' },
{ id: 2, name: 'John' },
{ id: 3, name: 'Sarah' },
{ id: 4, name: 'Carol' },
{ id: 5, name: 'Mike' }
];
}
// trackBy function first argument is always an index
trackById(index: number, user: { id: number, name: string }): number {
return item.id; // Unique identifier for each item
}