AngularJS input throttling with debounce

Original author: Jeremy Likness
  • Transfer
  • Tutorial
There are various scenarios for using throttling of the input so that the filter will not be recalculated every time the value changes, but less often. A more suitable term is “debounce”, since in essence you expect the value to stabilize at some constant level before calling the function so as not to cause “bounce” of constant requests to the server. A canonical case of this kind is a user entering text in an input field to filter a list of elements. If the logic of your filter includes some overhead (for example, filtering occurs through a REST resource that executes a query on the backend database), then you definitely won’t want to restart and reload the query results all the time while the user is writing text in the field. It’s more appropriate to wait instead,

A simple solution to this problem is here: jsfiddle.net/nZdgm

Imagine that you have a list ($ scope.list) that you publish as a filtered list ($ scope.filteredList) based on something containing text from the $ scope field. searchText. Your form would look something like this (don't pay attention to the throttle checkbox for now):

 Throttle {{searchText}}
  • {{item}}


A typical scenario is to observe the search field and respond instantly. Filtration Method:

var filterAction = function($scope) {
    if (_.isEmpty($scope.searchText)) {
        $scope.filteredList = $scope.list;
        return;
    }
    var searchText = $scope.searchText.toLowerCase();
    $scope.filteredList = _.filter($scope.list, function(item) {
        return item.indexOf(searchText) !== -1;
    });
};


The controller sets $ watch as follows:

$scope.$watch('searchText', function(){filterAction($scope);});


This approach will trigger filtering every time you type in a field. To fix the situation, use the debounce function built into underscore.js . The function is pretty simple: give it the function to execute and the time in milliseconds. This will delay the actual function call until the specified time has passed since the last attempt to call it. In other words, with a delay of 1 second (which I use in this example to exaggerate the effect) and a continuous stream of function calls during quick text input into the field, the real function will not be called until I stop typing from now on 1 second will not pass.

You might be tempted to do a simple debounce like this:

var filterThrottled = _.debounce(filterAction, 1000);
$scope.$watch('searchText', function(){filterThrottled($scope);});


However, there is a problem. This approach uses a timer that fires outside the $ digest cycle , so this ultimately will not affect the UI, because Angular does not know about the changes. Instead, you should wrap the call in $ apply :

var filterDelayed = function($scope) {
    $scope.$apply(function(){filterAction($scope);});
};


After that, you can set $ watch and react as soon as the input stops:

var filterThrottled = _.debounce(filterDelayed, 1000);
$scope.$watch('searchText', function(){filterThrottled($scope);});


Of course, a full-fledged example here should include throttling, so that you can see the difference between “instant” filtering and deferred. Fiddle for the case can be found here: jsfiddle.net/nZdgm

Also popular now: