Angular Abyss: NgFor optimization with trackBy magic
Dear NgReader, welcome to Angular Abyss series of articles made by me, which describe Angular’s most hot topics and common problems. Hope you enjoy!
All articles:
Problem:
We want to understand how *ngFor directive works and how it knows when it’s time to re-render DOM.
1. Setup
First of all, let’s create a project using Angular CLI.
ng new my-app
I am using VS Code that has a nice command line shortcut to open editor in current directory.
cd my-app && code .
2. Show list of objects
Open app.component.html file, delete all the code and paste this:
Now in app.component.ts write this code:
What have we done here? We have a list of items that need to be displayed. Like always we display items in template using *ngFor directive.
Also we have two buttons. One adds a new item to the list and second button replace the whole list. New list has the same items and one new in plus.
This simulates the situation when application retrieves a collection of some entities from API and then after sometime refresh the data in case any new items appeared.
Time to run the app and click the buttons. 🔍
ng serve -o
3. How DOM is changed?
To understand how ngFor re-renders HTML we need to open browser developer tools. I am using Chrome browser so for me its to press F12.
If I am an attentive person I notice changes (flashes) in HTML, when I click the buttons.
When you click a ‘Add item’ button you will see something like this:
And when you click ‘Replace list’ you see this:
What does it mean?
When you add a new item, only this one particular HTML needed for item is appended into page. Which is good.
When you replace the list, even it has almost identical objects, the whole list is re-rendered in DOM, wasting your browser resources. This can lead to performance issues when you display huge objects or long lists.
We expect that Angular will not re-render items that are already here. However, it can be done using trackBy.
4. Optimize loop with trackBy
Change template and add a trackBy to derivative:
Now in typescript file implement this function:
Now ngFor understands that list item has an unique property that can be tracked to know if something changed. Even item object has another reference but his tracking property and all other properties are not changed, it doesn't need to be re-rendered.
Now if you click on ‘Replace list’ button you will see this:
Summary: By default ngFor directive cannot understand which objects are old, if reference of collection changed. This means the whole list will be inserted again in DOM.
Use trackBy option with ngFor whenever is possible. It can save browser resources and increase performance.
See the code here.
🚀 If you read something interesting from that article, please like and follow me for more posts. Thank you NgReader! 😏