Undefined Rants

Code, Ramen and Avocado

Debounce Ng-repeat Search for Performance

One of the perfomance tips to increase performance when searching data from ng-repeat is to use debounce. In this short post I will use the debounce function provided by Lodash to apply filter for ng-repeat.

Why Debounce

First let’s look at the code without debounce.

Wow this actually blows off my head at first because I can see immediate response. However, when buiding real app, this is considered expensive. Let’s think this way. If a user type to search eg: Tokyo how many request we’are going to send(also imagine if this request is send to server). 5? One request per word? No we just want 1 request. The solution is to send request when we know that user finishes typing the word. This is when debounce comes in.

Using debounce

Using debounce is easy. From lodash documentation,

1
2
3
// avoid costly calculation while the window size is in flux
var lazyLayout = _.debounce(calculateLayout, 150);
jQuery(window).on('resize', lazyLayout);

Pretty straight forward. We want to use this in Angular JS. We will use $scope.$watch and $scope.$apply(best explaination on these two).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// HTML
<input type="search" ng-model="searchQuery" />
<div ng-repeat="d in data" | filter:search></div>

// Create a watcher to watch user input
$scope.$watch('searchQuery', _.debounce(function(newValue, oldValue){
    if(!newValue) return ;
    // update view after 1000ms when changes is detected
    $scope.$apply(function(){
        updateQuery();
    })
}, 1000));

var updateQuery = function(){
    $scope.search = $scope.searchQuery;
}

The concept here is to watch our user input and let Angular know when there is a change. Because lodash debounce is not included inside Angular dirty checking loop, so we have to tell Angular by using $scope.$watch and $scope.$apply. Here is the fiddle for above example.

It might be interesting if we add indicator like is loading … when waiting to send the request.

ng-model-options( 1.3.0 )

Yes in Angular v1.3.0, we do not need to do the $scope.$watch thing for debouncing. A super handy attribute can just do all the thing, that’s is ng-model-options. Code above can be shortened as:

1
2
<input type="search" ng-model="search" ng-model-options="{ debounce: 1000 }" />
<div ng-repeat="d in data | filter:search"></div>

Making life easier.

Comments