When dealing with large lists rendered using ng-repeat
in AngularJS, UI lag is a common issue, as AngularJS needs to check for changes and render the entire list. This can significantly impact performance, especially when the list is large or updated frequently. Here are steps you can take to optimize the performance of large ng-repeat
lists:
1. Use track by
with ng-repeat
One of the best ways to optimize rendering in ng-repeat
is by using the track by
expression. When you use track by
, AngularJS can track each item by a unique identifier (usually an ID), and it will only re-render the items that have changed. This prevents AngularJS from re-rendering the entire list when a change occurs.
<div ng-repeat="item in items track by item.id">
<!-- Your item content -->
</div>
By adding track by item.id
, AngularJS will track each item by its id
instead of the item’s entire object, which can drastically reduce the re-rendering overhead.
2. Use ng-if
instead of ng-show
/ng-hide
If you have dynamically added or removed items from the list, it’s more efficient to use ng-if
to remove the items from the DOM entirely rather than toggling visibility with ng-show
or ng-hide
.
<div ng-repeat="item in items">
<div ng-if="item.isVisible">
<!-- Item content -->
</div>
</div>
ng-if
removes the element from the DOM when the condition is false, preventing unnecessary watchers from being created, whereas ng-show
/ng-hide
only hides the element and leaves it in the DOM.
3. Implement Pagination or Infinite Scrolling
Rendering large lists all at once can be very slow. Instead of rendering the entire list, you can break the list into smaller chunks, either through pagination or infinite scrolling. This reduces the number of DOM elements rendered at once.
Example: Infinite Scrolling
You can use a scroll event to load additional data as the user scrolls down the page.
<div ng-repeat="item in visibleItems">
<!-- Your item content -->
</div>
<!-- Button for pagination or trigger -->
<button ng-click="loadMore()">Load More</button>
Controller Example:
$scope.visibleItems = [];
$scope.items = []; // Full list of items
$scope.loadMore = function() {
let newItems = $scope.items.slice($scope.visibleItems.length, $scope.visibleItems.length + 20);
$scope.visibleItems = $scope.visibleItems.concat(newItems);
};
With this approach, only a small set of items is rendered initially, and additional items are added as the user scrolls down.
4. Use one-time
Binding
If the data you’re displaying doesn’t change after it’s initially rendered (such as static data or initial data load), you can use one-time binding ::
to prevent AngularJS from creating watchers on the data. This can greatly improve performance by reducing the number of watchers created.
<div ng-repeat="item in items">
<span>{{::item.name}}</span>
</div>
The ::
syntax tells AngularJS to bind the value only once, and then it won’t watch for changes, reducing the overhead of the digest cycle.
5. Avoid Expensive Expressions in ng-repeat
Ensure that the expressions inside your ng-repeat
are not too complex or computationally expensive. For example, performing calculations or calling functions within the ng-repeat
can be expensive, as AngularJS will evaluate them on every digest cycle.
<div ng-repeat="item in items">
<span>{{ item.name | uppercase }}</span> <!-- avoid complex filters or calculations -->
</div>
Instead of using filters or functions inside ng-repeat
, pre-compute values in your controller to reduce unnecessary evaluations during each digest cycle.
6. Use Lazy Loading for Images
If your list contains images or other media, you can use lazy loading to only load images when they are visible in the viewport. This reduces the initial load time of your application and avoids rendering unnecessary images that the user may never see.
You can implement lazy loading using a third-party library or custom logic, such as the ng-lazyload-image
directive.
7. Virtualization
Virtualization (or windowing) is a technique where only the visible portion of the list is rendered, and elements outside the viewport are not rendered at all. This can significantly improve performance when dealing with large lists.
For example, you could use libraries like angular-ui-grid or ng-infinite-scroll to implement this. These libraries handle rendering only the visible part of the list, greatly improving performance.
8. Minimize the Use of $watch()
in ng-repeat
Every time you use ng-repeat
, AngularJS automatically sets up a watcher for each element in the list. With a large list, this can lead to excessive watchers, causing a performance hit. Minimize $watch()
usage, especially within ng-repeat
.
If you must watch for changes in the list, ensure you are using optimized watchers, such as $watchCollection()
instead of $watch()
when observing arrays or objects.
$scope.$watchCollection('items', function(newValue, oldValue) {
// Handle changes to items
});
This will only track changes in the array’s length or its content, which can be more efficient than using $watch()
.
9. Debounce User Inputs
If your ng-repeat
list is being filtered or updated based on user input, debounce the input to reduce the frequency of list updates. For example, if you have a search feature, debounce the input so that AngularJS doesn’t update the list on every keystroke.
$scope.search = function() {
$timeout(function() {
// Perform search/filter logic
}, 300); // 300ms debounce time
};
This reduces the frequency of updates, which can help prevent UI lag when handling large datasets.
10. Optimize the Digest Cycle
AngularJS’s digest cycle can be expensive if there are too many watchers. To optimize the digest cycle:
- Minimize Watchers: Remove unnecessary watchers or use
ng-if
to only include DOM elements when necessary. - Use
$scope.$applyAsync()
: Instead of triggering the digest cycle synchronously with$scope.$apply()
, use$scope.$applyAsync()
to schedule it later, reducing immediate overhead.