Angular ngFor trackBy

Angular ngFor trackBy

In this article, I am going to discuss the Angular ngFor trackBy with some examples. The ngFor trackBy is used to improve the performance of an angular application. As part of this article, we are going to discuss the following things in a greater detail.

  1. Using the Angular trackyBy with ngFor directive
  2. How to get the index of an item in a collection
  3. Identifying the first and the last elements in a collection
  4. How to Identify the even and odd elements in a collection

Let us discuss each of the above points one by one.

We are going to work with the same example that we worked in the previous article. So please read our previous article before proceeding to this article.

Using the Angular ngFor trackyBy: 

The Angular ngFor directive may perform poorly with the large collections.  A small change to the collection such as adding a new item or removing an existing item from the collection may trigger a cascade of DOM manipulations

Let us understand this concept with an example. Please modify the StudentList.Component.ts file as shown below.

The constructor() of the StudentList Component initializes the students’ collection property with 3 students objects, on the other hand, the getStudents() method of the StudentList Component returns another collection of 5 student objects (The 3 existing students plus another two new student object) 

import { Component } from '@angular/core';

@Component({
    selector: 'student-list',
    templateUrl: 'app/Student/StudentList.Component.html',
    styleUrls: ['app/Student/StudentList.Component.css']
})

export class StudentListComponent { 
    students: any[];

    constructor() {
        this.students = [
            {
                ID: 'std101', FirstName: 'Pranaya', LastName: 'Rout', Branch: 'CSE',
                DOB: '29/02/1988', Gender: 'Male', Mobile: 9876543210
            },
            {
                ID: 'std102', FirstName: 'Anurag', LastName: 'Mohanty', Branch: 'ETC',
                DOB: '23/05/1989', Gender: 'Male', Mobile: 1112223334
            },
            {
                ID: 'std103', FirstName: 'Priyanka', LastName: 'Dewangan', Branch: 'CSE',
                DOB: '24/07/1992', Gender: 'Female', Mobile: 1234567890
            },
        ];
    }

    getStudents(): void {
        this.students = [
            {
                ID: 'std101', FirstName: 'Pranaya', LastName: 'Rout', Branch: 'CSE',
                DOB: '29/02/1988', Gender: 'Male', Mobile: 9876543210
            },
            {
                ID: 'std102', FirstName: 'Anurag', LastName: 'Mohanty', Branch: 'ETC',
                DOB: '23/05/1989', Gender: 'Male', Mobile: 1112223334
            },
            {
                ID: 'std103', FirstName: 'Priyanka', LastName: 'Dewangan', Branch: 'CSE',
                DOB: '24/07/1992', Gender: 'Female', Mobile: 1234567890
            },
            {
                ID: 'std104', FirstName: 'Hina', LastName: 'Sharma', Branch: 'ETC',
                DOB: '19/08/1990', Gender: 'Female', Mobile: 3334445550
            },
            {
                ID: 'std105', FirstName: 'Sambit', LastName: 'Satapathy', Branch: 'CSE',
                DOB: '12/94/1991', Gender: 'Male', Mobile: 9998887770
            }
        ];
    } 
}

Now let us modify the code in StudentList.Component.html file as shown below.

<table>
    <thead>
        <tr>
            <th>ID</th>
            <th>First Name</th>
            <th>Last Name</th>
            <th>Branch</th>
            <th>DOB</th>
            <th>Gender</th>
            <th>Mobile</th>
        </tr>
    </thead>
    <tbody>
        <tr *ngFor='let student of students'>
            <td>{{student.ID}}</td>
            <td>{{student.FirstName}}</td>
            <td>{{student.LastName}}</td>
            <td>{{student.Branch}}</td>
            <td>{{student.DOB}}</td>
            <td>{{student.Gender}}</td>
            <td>{{student.Mobile}}</td>
        </tr>
        <tr *ngIf="!students || students.length==0">
            <td colspan="7">
                No Students to display
            </td>
        </tr>
    </tbody>
</table>
<br />
<button (click)='getStudents()'>Refresh Students</button>
Understanding the above Code:

In the above HTML, at the moment we are not using the trackBy with ngFor directive. So when the page loads for the first time we will see the 3 students which are initialized through the constructor of the StudentList Components but when we click on the “Refresh Students” button, then we will see the 4th and 5th students as well.

It looks like it just added the additional rows for the 4th and 5th students but that’s not true, it effectively destroyed all the <tr> and <td> elements of all the students and then recreated them.

To confirm this launch the browser developer tools by pressing the F12 key. Then click on the “Elements” tab and expand the <table> and then <tbody> elements.

At this point click on the “Refresh Students” button and you will notice that all the <tr> elements are briefly highlighted indicating that they are destroyed and recreated as shown in the below image.

Angular ngFor trackBy

This is because Angular by default keeps track of the objects using the object references. So, when we click on the “Refresh Students” button we get different object references and as a result, Angular has no choice but to delete all the old DOM elements and insert the new DOM elements. 

To overcome the above problem, the Angular comes with a solution that is trackBy. The trackBy function in Angular takes the current item as an argument and then it returns a unique identifier using which we can track that item. In our example, we are going to track by Student ID as the student id is unique for each student.

To do this, add the following method to StudentList.Component.ts.  
trackByStudentID(student: any): string {
    return student.ID;
}

Then do the following changes in the StudentList.Component.html file:

<tr *ngFor=’let student of students; trackBy:trackByStudentID’>

Here we are using the trackBy along with ngFor directive.

At this point, run the application and then launch the browser developer tools by pressing the F12 key. When you click on the “Refresh Students” button for the first time, then you can notice that only the 4th and 5th rows of the students are highlighted indicating that only those <tr> elements are added.

On the subsequent clicks, nothing is highlighted meaning none of the <tr> elements are destroyed or added as the students’ collection has not changed. Each and every time when we click on the “Refresh Students” button we get different object references, but as the Angular is now tracking the student objects using the Student Id instead of object references, as a result, the respective DOM elements are not affected. 

How to get the index of an item in a collection?

We can get the index of an item in a collection using the index property of the ngFor directive. Let us understand this with an example.

Please modify the StudentList.Component.html file as shown below

<table>
    <thead>
        <tr>
            <th>ID</th>
            <th>First Name</th>
            <th>Last Name</th>
            <th>Branch</th>
            <th>DOB</th>
            <th>Gender</th>
            <th>Mobile</th>
            <th>Index</th>
        </tr>
    </thead>
    <tbody>
        <tr *ngFor='let student of students; trackBy:trackByStudentID; let i=index'>
            <td>{{student.ID}}</td>
            <td>{{student.FirstName}}</td>
            <td>{{student.LastName}}</td>
            <td>{{student.Branch}}</td>
            <td>{{student.DOB}}</td>
            <td>{{student.Gender}}</td>
            <td>{{student.Mobile}}</td>
            <td>{{i}}</td>
        </tr>
        <tr *ngIf="!students || students.length==0">
            <td colspan="7">
                No Students to display
            </td>
        </tr>
    </tbody>
</table>
<br />
<button (click)='getStudents()'>Refresh Students</button>

Notice that in the above code, we are using the index property of the Angular ngFor directive to store the index position of an element in a template input variable “i”. The variable “i” is then used in the <td> element where we want to display the index value. We used the let keyword to create the template input variable “i”.  The let keyword in angular is very much similar to the var keyword that we used in C# programming.

The index of an element is extremely useful when you are creating the HTML elements dynamically. We will discuss creating the HTML elements dynamically in our upcoming articles. 

Now run the application and then you will notice that the index of the element is displayed in the last <td> element as shown in the below image.

Angular ngFor Index property

How to identify the first and the last elements in a collection?

To identify the First and Last elements in a collection, you need to use the first and last properties of the ngFor directive respectively. Let us understand this with an example.

Please modify the StudentList.Component.html file as shown below

<table>
    <thead>
        <tr>
            <th>ID</th>
            <th>First Name</th>
            <th>Last Name</th>
            <th>Branch</th>
            <th>DOB</th>
            <th>Gender</th>
            <th>Mobile</th>
            <th>Index</th>
            <th>IsFirst</th>
            <th>IsLast</th>
        </tr>
    </thead>
    <tbody>
        <tr *ngFor='let student of students; trackBy:trackByStudentID; let i=index; let isFirst = first; let isLast = last'>
            <td>{{student.ID}}</td>
            <td>{{student.FirstName}}</td>
            <td>{{student.LastName}}</td>
            <td>{{student.Branch}}</td>
            <td>{{student.DOB}}</td>
            <td>{{student.Gender}}</td>
            <td>{{student.Mobile}}</td>
            <td>{{i}}</td>
            <td>{{isFirst}}</td>
            <td>{{isLast}}</td>
        </tr>
        <tr *ngIf="!students || students.length==0">
            <td colspan="7">
                No Students to display
            </td>
        </tr>
    </tbody>
</table>
<br />
<button (click)='getStudents()'>Refresh Students</button>

Now run the application and it will give you the below output.

Angular ngFor Index property

How to identify the even and odd elements in a collection?

To identify the Event and Odd elements in a collection, you need to use the even and odd properties of the ngFor directive respectively. Let us understand this with an example.

Please modify the StudentList.Component.html file as shown below

<table>
    <thead>
        <tr>
            <th>ID</th>
            <th>First Name</th>
            <th>Last Name</th>
            <th>Branch</th>
            <th>DOB</th>
            <th>Index</th>
            <th>IsFirst</th>
            <th>IsLast</th>
            <th>IsEven</th>
            <th>IsOdd</th>
        </tr>
    </thead>
    <tbody>
        <tr *ngFor='let student of students; trackBy:trackByStudentID; let i=index; let isFirst = first; let isLast = last, let isEven = even; let isOdd = odd'>
            <td>{{student.ID}}</td>
            <td>{{student.FirstName}}</td>
            <td>{{student.LastName}}</td>
            <td>{{student.Branch}}</td>
            <td>{{student.DOB}}</td>
            <td>{{i}}</td>
            <td>{{isFirst}}</td>
            <td>{{isLast}}</td>
            <td>{{isEven}}</td>
            <td>{{isOdd}}</td>
        </tr>
        <tr *ngIf="!students || students.length==0">
            <td colspan="10">
                No Students to display
            </td>
        </tr>
    </tbody>
</table>
<br />
<button (click)='getStudents()'>Refresh Students</button>

Now when you run the application, it will give you the below output.

Angular ngFor Index property

In the next article, I will discuss Angular Pipes with examples.

SUMMARY:

In this article, I try to explain the Angular ngFor trackBy with examples. I hope this article will help you with your need. I would like to have your feedback. Please post your feedback, question, or comments about this article.

No HTML was returned.

Leave a Reply

Your email address will not be published. Required fields are marked *