Optimizing Angular Performance with `trackBy` in `ngFor`

Manthan Ankolekar - Jul 8 - - Dev Community

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;
  }
}
Enter fullscreen mode Exit fullscreen mode
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 { }
Enter fullscreen mode Exit fullscreen mode

Stackblitz Link

How It Works

  1. Component Template: The template uses an ngFor directive to iterate over the items array. The trackBy function is specified as trackById.

  2. Component Class:

    • items is an array of objects, each with a unique id and a name.
    • The updateList method updates the list, simulating a scenario where the list changes dynamically.
    • The trackById function returns the id of each item, providing a unique identifier for Angular to track.

Benefits of Using trackBy

  1. Improved Performance: By uniquely identifying each item, Angular can avoid unnecessary re-rendering, leading to faster updates and smoother user experiences.
  2. Efficient DOM Manipulation: Only the items that have changed are updated in the DOM, reducing the workload for the browser.
  3. 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
Enter fullscreen mode Exit fullscreen mode

2. Generate Components

Generate two components, one for each example:

ng generate component without-trackby
ng generate component with-trackby
Enter fullscreen mode Exit fullscreen mode

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' }
    ];
  }
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode
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;
  }
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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 { }
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

6. Run the Application

Run the application to see both components in action:

ng serve
Enter fullscreen mode Exit fullscreen mode

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.

Stackblitz Link

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!


. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .