Failure to use track by in ng-repeat leading to slow rendering

Loading

In AngularJS, ng-repeat is used to loop over collections and render elements dynamically. However, failing to use track by can lead to performance issues, slow rendering, and unnecessary DOM manipulations. This article will cover:

  • How ng-repeat works internally
  • Why missing track by causes performance degradation
  • Real-world examples of the problem
  • How track by solves the issue
  • Best practices for optimal rendering

1. Understanding ng-repeat and Its Performance Implications

How ng-repeat Works Without track by

ng-repeat loops over an array and generates a list of elements in the DOM. However, by default, AngularJS tracks objects by reference, not by a unique identifier.

Basic ng-repeat Example Without track by

<ul>
<li ng-repeat="item in items">{{ item.name }}</li>
</ul>

When the items array changes, AngularJS recreates the entire DOM list, even if only one item was modified. This is inefficient, especially for large datasets.


2. Why Not Using track by Leads to Slow Rendering

When the list updates, AngularJS follows these steps:

1️⃣ Compares each element in the new array with the previous array.
2️⃣ Identifies which elements were added, removed, or modified.
3️⃣ Removes old elements and re-renders the updated elements.

Problem: AngularJS Tracks Objects by Reference

Since objects in JavaScript don’t have inherent IDs, Angular cannot differentiate elements efficiently. If you push a new item or modify an existing one, Angular destroys and recreates the entire list instead of updating specific elements.


3. Example of Performance Issue Without track by

Consider this scenario where items are dynamically updated:

<ul>
<li ng-repeat="item in items">{{ item.name }}</li>
</ul>
<button ng-click="addItem()">Add Item</button>

Controller:

$scope.items = [
{ name: "Apple" },
{ name: "Banana" },
{ name: "Cherry" }
];

$scope.addItem = function () {
$scope.items.push({ name: "Mango" });
};

Every time addItem() is called, Angular destroys all existing DOM elements and re-renders them, even if only one item was added.


4. Solution: Using track by for Better Performance

track by provides a unique identifier to track each element efficiently.

Optimized Example With track by

<ul>
<li ng-repeat="item in items track by $index">{{ item.name }}</li>
</ul>

Now, AngularJS only updates the modified elements instead of re-rendering the entire list.


5. Why track by Improves Performance

Prevents full list re-rendering – Only updates changed elements.
Reduces unnecessary DOM operations – No need to destroy/recreate elements.
Enhances UI responsiveness – Faster updates, especially in large lists.

Performance Difference:

Without track byWith track by
Entire list re-rendersOnly changed items update
Slower for large dataFaster rendering
Increased CPU & memory usageOptimized performance

6. Using track by with Unique IDs

If each item has a unique id, use that instead of $index for even better performance.

Best Practice – Track by Unique ID

<ul>
<li ng-repeat="item in items track by item.id">{{ item.name }}</li>
</ul>

Example Data:

$scope.items = [
{ id: 1, name: "Apple" },
{ id: 2, name: "Banana" },
{ id: 3, name: "Cherry" }
];

This ensures that Angular tracks items based on their actual identity instead of just their position in the array.


7. Common Mistakes and Fixes

Mistake 1: Using track by $index for Dynamic Lists

If elements are frequently added, removed, or sorted, using $index can still cause issues.

Problem:

<li ng-repeat="item in items track by $index">{{ item.name }}</li>

When sorting or removing items, $index changes, leading to incorrect tracking.

Fix: Always use a unique identifier:

<li ng-repeat="item in items track by item.id">{{ item.name }}</li>

8. When to Use track by

ScenarioUse track by?Recommended Key
Static list (never changes) NoN/A
List updates frequently YesUnique id
Sorting items dynamically YesUnique id
Large datasets (500+ items) YesUnique id

Leave a Reply

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