121
votes

I have an input which filters a ng-repeat list on change. The repeat contains a lot of data and takes a few seconds to filter through everything. I would like their to be 0.5 second delay before I start the filtering process. What is the correct way in angular to create this delay?

Input

 <input ng-model="xyz" ng-change="FilterByName()" />

Repeat

 <div ng-repeat"foo in bar">
      <p>{{foo.bar}}</p>
 </div>

Filter Function

 $scope.FilterByName = function () {
      //Filtering Stuff Here
 });

Thanks

4
Just use a $timeout for 500ms. $scope.FilterByName = function () { $timeout(_filterByName , 500) - PSL
@PSL where in the function? I only want the search to execute once. If I just offset it, it will just create a bigger delay and do multiple searches. - MGot90
Yeah, in your function. prev comment has a snippet. You can use $timeout.cancel(timeoutpromise) if one timeout is in progress and another change gets triggered. - PSL
@PSL Thank you works like a charm! - MGot90

4 Answers

284
votes

AngularJS 1.3+

Since AngularJS 1.3 you can utilize the debounce property ngModelOptions provides to achieve that very easy without using $timeout at all. Here's an example:

HTML:

<div ng-app='app' ng-controller='Ctrl'>
    <input type='text' placeholder='Type a name..'
        ng-model='vm.name'
        ng-model-options='{ debounce: 1000 }'
        ng-change='vm.greet()'
    />

    <p ng-bind='vm.greeting'></p>
</div>

JS:

angular.module('app', [])
.controller('Ctrl', [
    '$scope',
    '$log',
    function($scope, $log){
        var vm = $scope.vm = {};

        vm.name = '';
        vm.greeting = '';
        vm.greet = function greet(){
            vm.greeting = vm.name ? 'Hey, ' + vm.name + '!' : '';
            $log.info(vm.greeting);
        };
    }
]);

-- OR --

Check the Fiddle

Before AngularJS 1.3

You'll have to use $timeout to add a delay and probably with the use of $timeout.cancel(previoustimeout) you can cancel any previous timeout and run the new one(helps to prevent the filtering to be executed multiple times consecutovely within a time interval)

Here is an example:

app.controller('MainCtrl', function($scope, $timeout) {
    var _timeout;

    //...
    //...

    $scope.FilterByName = function() {
        if(_timeout) { // if there is already a timeout in process cancel it
            $timeout.cancel(_timeout);
        }
        _timeout = $timeout(function() {
            console.log('filtering');
            _timeout = null;
        }, 500);
    }
});
22
votes

You could use $timeout to add a delay and probably with the use of $timeout.cancel(previoustimeout) you can cancel any previous timeout and run the new one(helps to prevent the filtering to be executed multiple times consecutovely within a time interval)

Example:-

app.controller('MainCtrl', function($scope, $timeout) {
  var _timeout;

 //...
 //...

  $scope.FilterByName = function () {
    if(_timeout){ //if there is already a timeout in process cancel it
      $timeout.cancel(_timeout);
    }
    _timeout = $timeout(function(){
      console.log('filtering');
      _timeout = null;
    },500);
  }
 });

Plnkr

8
votes

I know the question is too old. But still want to provide one quicker way to achieve this using debouncing.

So the code can be written as

<input ng-model="xyz" ng-change="FilterByName()" ng-model-options="{debounce: 500}"/>

Debounce will take the number in milliseconds.

0
votes

or you can use directive ' typeahead-wait-ms="1000" ' from angular-ui

<input 
   typeahead="name for name in filterWasChanged()"
   typeahead-wait-ms="1000"
   type="text" placeholder="search"
   class="form-control" style="text-align: right" 
   ng-model="templates.model.filters.name">