Optimizing Angular Performance with `trackBy` in `ngFor`
Tags: webdev, angular, programming, beginners
Published: 2024-07-08 07:30:00

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.

```typescript
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`:

```typescript
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 { }
```

[Stackblitz Link](https://stackblitz.com/edit/stackblitz-starters-bzmdu7?file=src%2Fmain.ts)

#### 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:

```bash
ng new trackby-example
cd trackby-example
```

#### 2. Generate Components

Generate two components, one for each example:

```bash
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:**

```typescript
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:**

```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:**

```typescript
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:**

```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:

```typescript
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:

```html
<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:

```bash
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.

[Stackblitz Link](https://stackblitz.com/edit/stackblitz-starters-ua4tmn?file=src%2Fmain.ts)

#### 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!

---