Optimizing Angular Performance with `trackBy` in `ngFor`
Manthan Ankolekar
Posted on July 8, 2024
In any dynamic web application, managing and displaying lists efficiently is crucial for performance. Angular's ngFor
directive is a powerful tool for iterating over lists and rendering items in the DOM. However, when dealing with large or frequently changing lists, performance can become a concern. This is where Angular's trackBy
function comes into play.
What is trackBy
?
The trackBy
function is used with the ngFor
directive to help Angular uniquely identify items in a list. By default, Angular uses object identity to track changes, which can be inefficient. Using trackBy
, you can specify a unique identifier for each item, enabling Angular to optimize DOM manipulations and improve performance.
Why Use trackBy
?
Without trackBy
, Angular will recreate DOM elements for the entire list whenever it detects changes, even if only one item has changed. This can lead to unnecessary re-rendering and degraded performance, especially with large lists. trackBy
allows Angular to track items by a unique identifier, minimizing DOM updates to only the items that have changed.
Implementing trackBy
in Angular
Let's walk through a simple example to demonstrate how to use trackBy
in an Angular application.
Step 1: Define the Component
First, create a component that will display and update a list of items.
import { Component } from '@angular/core';
@Component({
selector: 'app-track-by-example',
template: `
<div>
<button (click)="updateList()">Update List</button>
<ul>
<li *ngFor="let item of items; trackBy: trackById">
{{ item.id }} - {{ item.name }}
</li>
</ul>
</div>
`,
styles: []
})
export class TrackByExampleComponent {
items = [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
{ id: 3, name: 'Item 3' }
];
updateList() {
this.items = [
{ id: 1, name: 'Updated Item 1' },
{ id: 2, name: 'Updated Item 2' },
{ id: 3, name: 'Updated Item 3' },
{ id: 4, name: 'New Item 4' }
];
}
trackById(index: number, item: any): number {
return item.id;
}
}
Step 2: Add the Component to a Module
Ensure that the component is declared in a module, such as app.module.ts
:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { TrackByExampleComponent } from './track-by-example.component';
@NgModule({
declarations: [
AppComponent,
TrackByExampleComponent
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
How It Works
Component Template: The template uses an
ngFor
directive to iterate over theitems
array. ThetrackBy
function is specified astrackById
.-
Component Class:
-
items
is an array of objects, each with a uniqueid
and aname
. - The
updateList
method updates the list, simulating a scenario where the list changes dynamically. - The
trackById
function returns theid
of each item, providing a unique identifier for Angular to track.
-
Benefits of Using trackBy
- Improved Performance: By uniquely identifying each item, Angular can avoid unnecessary re-rendering, leading to faster updates and smoother user experiences.
- Efficient DOM Manipulation: Only the items that have changed are updated in the DOM, reducing the workload for the browser.
-
Scalability: As the application grows and the lists become larger,
trackBy
ensures that performance remains optimal.
To demonstrate the performance difference between using trackBy
and not using trackBy
in an Angular application, we'll create two simple apps. Each app will have a list of items that gets updated when a button is clicked. One app will use trackBy
to optimize performance, while the other will not.
Step-by-Step Guide to compare both without trackby
& without trackby
1. Set Up the Angular Project
First, create a new Angular project if you don't already have one:
ng new trackby-example
cd trackby-example
2. Generate Components
Generate two components, one for each example:
ng generate component without-trackby
ng generate component with-trackby
3. Implement the Components
Component Without trackBy
Edit the without-trackby.component.ts
and without-trackby.component.html
files as follows:
without-trackby.component.ts:
import { Component } from '@angular/core';
@Component({
selector: 'app-without-trackby',
templateUrl: './without-trackby.component.html',
styleUrls: ['./without-trackby.component.css']
})
export class WithoutTrackbyComponent {
items = [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
{ id: 3, name: 'Item 3' }
];
updateList() {
this.items = [
{ id: 1, name: 'Updated Item 1' },
{ id: 2, name: 'Updated Item 2' },
{ id: 3, name: 'Updated Item 3' },
{ id: 4, name: 'New Item 4' }
];
}
}
without-trackby.component.html:
<div>
<h2>Without trackBy</h2>
<button (click)="updateList()">Update List</button>
<ul>
<li *ngFor="let item of items">
{{ item.id }} - {{ item.name }}
</li>
</ul>
</div>
Component With trackBy
Edit the with-trackby.component.ts
and with-trackby.component.html
files as follows:
with-trackby.component.ts:
import { Component } from '@angular/core';
@Component({
selector: 'app-with-trackby',
templateUrl: './with-trackby.component.html',
styleUrls: ['./with-trackby.component.css']
})
export class WithTrackbyComponent {
items = [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
{ id: 3, name: 'Item 3' }
];
updateList() {
this.items = [
{ id: 1, name: 'Updated Item 1' },
{ id: 2, name: 'Updated Item 2' },
{ id: 3, name: 'Updated Item 3' },
{ id: 4, name: 'New Item 4' }
];
}
trackById(index: number, item: any): number {
return item.id;
}
}
with-trackby.component.html:
<div>
<h2>With trackBy</h2>
<button (click)="updateList()">Update List</button>
<ul>
<li *ngFor="let item of items; trackBy: trackById">
{{ item.id }} - {{ item.name }}
</li>
</ul>
</div>
4. Update the App Module
Update the app.module.ts
to include the new components:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { WithoutTrackbyComponent } from './without-trackby/without-trackby.component';
import { WithTrackbyComponent } from './with-trackby/with-trackby.component';
@NgModule({
declarations: [
AppComponent,
WithoutTrackbyComponent,
WithTrackbyComponent
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
5. Update the Main App Component
Update app.component.html
to display both components:
<div style="text-align:center">
<h1>Angular trackBy Example</h1>
</div>
<app-without-trackby></app-without-trackby>
<app-with-trackby></app-with-trackby>
6. Run the Application
Run the application to see both components in action:
ng serve
Navigate to http://localhost:4200
in your browser. You will see two sections, one without trackBy
and one with trackBy
. Click the "Update List" button in each section and observe the differences in performance and DOM updates.
Conclusion
By implementing these two components, you can observe how using trackBy
helps Angular to optimize DOM manipulations and improve performance. This is particularly noticeable with larger lists or more complex applications, where the efficiency gains become more significant.
Feel free to expand this example with more complex data or additional functionalities to see how trackBy
can benefit your Angular projects.
Using trackBy
with ngFor
is a simple yet powerful way to optimize the performance of your Angular applications. By uniquely identifying items in a list, you can minimize DOM manipulations and ensure that your application remains responsive and efficient, even as it scales. Implementing trackBy
is straightforward and can make a significant difference, particularly for applications that handle large or frequently changing lists.
Start using trackBy
in your Angular projects today and experience the performance benefits for yourself!
Posted on July 8, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.