Today we will look at the trackBy directive and how to use it to improve the performance and speed of our apps.
When we build Angular apps, one of the most-used directives to show a list of items is ngFor
, but do you know what happens when the data changes and how Angular works with the changes?
TrackBy
is a directive that can work with ngFor
to identify items in a list of DOM elements like a list or array to perform unique updates in the DOM, improving the speed and performance.
This post will look at why to use the trackBy
directive in Angular and how it helps with application performance.
When we need to render a list of items, the ngFor
directive helps iterate over arrays or iterable objects and show them in the HTML template.
Let’s build a small example app that shows a list of colors to understand how it works. We have a default list with more than 600 colors with their hex codes as their unique IDs.
import { Component, VERSION } from '@angular/core';
import { COLORS } from './colors';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
colors = COLORS;
}
In our template, we use the ngFor
directive to show the list of colors.
<ul>
<li *ngFor="let color of colors">
{{ color.name }}
</li>
</ul>
What happened here? Nothing special, but Angular created one DOM node for each item in the array. What happens if we add a new color to our array? What happens with all the nodes?
In our template, we add input and a button. The input uses #colorName to reference it, and the button uses the function addColor()
to send the value of colorName input and push it to the array.
<div>
<input #colorName /><button (click)="addColor(colorName.value)">Add</button>
<ul>
<li *ngFor="let color of colors">
{{ color.name }}
</li>
</ul>
</div>
Create the new method addColor
with the parameter name in the TypeScript file. We create a new object newColor
, and assign it to the colors
array.
addColor(input: HTMLInputElement): void {
if (!input.value) {
return;
}
const newColor = { name: input.value, hex: Math.random().toString() };
this.colors = [...this.colors, newColor];
input.value = '';
}
The method adds the new color to the list; when the array changes, Angular needs to re-render all nodes in the DOM, and it is a heavy operation impacting our apps.
Because the ngFor
directive doesn’t have a unique ID or key to know which element changed or modified, Angular needs to find how to detect the unique item in the array to commit safe DOM changes.
We want to update only the DOM element affected by the change with a unique ID that easily keeps the DOM UI state or selected item because the unique ID helps to track the specific item.
How can we tell Angular to trigger the change in the required item?
Angular came up with the trackBy
directive, which is optionally passed into ngFor
to help identify unique items in our arrays.
TrackBy
and ngFor
together allow Angular to detect the specific node element that needs to change or be added instead of rebuilding the whole array.
TrackBy
expects a function to compare the items. It needs to be a pure function without side effects, return a unique value and be fast.
First, we create a new function colorTrackBy
that takes the color and returns the unique hex as ID.
colorTrackBy(index, color) {
return color.hex;
}
Use trackBy
and pass the colorTrackBy
function as a parameter in our HTML.
<ul>
<li *ngFor="let color of colors; trackBy: colorTrackBy">
{{ color.name }}
</li>
</ul>
Perfect! NgFor has a way to track the items, and Angular performs unique updates in our DOM!
If you want to see the final version of the code, feel free to play in https://stackblitz.com/edit/angular-ivy-hqboys?file=src%2Fapp%2Fapp.component.html.
We have seen how the trackBy
directive works together with ngFor
, why it is crucial when rendering an extensive list of elements without impacting the performance of our apps, and how to build our custom trackBy
functions.
Dany Paredes is a Google Developer Expert on Angular and Progress Champion. He loves sharing content and writing articles about Angular, TypeScript and testing on his blog and on Twitter (@danywalls).