Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to put a delay on AngularJS instant search?

UPDATE

Now it's easier than ever (Angular 1.3), just add a debounce option on the model.

<input type="text" ng-model="searchStr" ng-model-options="{debounce: 1000}">

Updated plunker:
http://plnkr.co/edit/4V13gK

Documentation on ngModelOptions:
https://docs.angularjs.org/api/ng/directive/ngModelOptions

Old method:

Here's another method with no dependencies beyond angular itself.

You need set a timeout and compare your current string with the past version, if both are the same then it performs the search.

$scope.$watch('searchStr', function (tmpStr)
{
  if (!tmpStr || tmpStr.length == 0)
    return 0;
   $timeout(function() {

    // if searchStr is still the same..
    // go ahead and retrieve the data
    if (tmpStr === $scope.searchStr)
    {
      $http.get('//echo.jsontest.com/res/'+ tmpStr).success(function(data) {
        // update the textarea
        $scope.responseData = data.res; 
      });
    }
  }, 1000);
});

and this goes into your view:

<input type="text" data-ng-model="searchStr">

<textarea> {{responseData}} </textarea>

The mandatory plunker: http://plnkr.co/dAPmwf


(See answer below for a Angular 1.3 solution.)

The issue here is that the search will execute every time the model changes, which is every keyup action on an input.

There would be cleaner ways to do this, but probably the easiest way would be to switch the binding so that you have a $scope property defined inside your Controller on which your filter operates. That way you can control how frequently that $scope variable is updated. Something like this:

JS:

var App = angular.module('App', []);

App.controller('DisplayController', function($scope, $http, $timeout) {
    $http.get('data.json').then(function(result){
        $scope.entries = result.data;
    });

    // This is what you will bind the filter to
    $scope.filterText = '';

    // Instantiate these variables outside the watch
    var tempFilterText = '',
        filterTextTimeout;
    $scope.$watch('searchText', function (val) {
        if (filterTextTimeout) $timeout.cancel(filterTextTimeout);

        tempFilterText = val;
        filterTextTimeout = $timeout(function() {
            $scope.filterText = tempFilterText;
        }, 250); // delay 250 ms
    })
});

HTML:

<input id="searchText" type="search" placeholder="live search..." ng-model="searchText" />
<div class="entry" ng-repeat="entry in entries | filter:filterText">
    <span>{{entry.content}}</span>
</div>

In Angular 1.3 I would do this:

HTML:

<input ng-model="msg" ng-model-options="{debounce: 1000}">

Controller:

$scope.$watch('variableName', function(nVal, oVal) {
    if (nVal !== oVal) {
        myDebouncedFunction();
    }
});

Basically you're telling angular to run myDebouncedFunction(), when the the msg scope variable changes. The attribute ng-model-options="{debounce: 1000}" makes sure that msg can only update once a second.


 <input type="text"
    ng-model ="criteria.searchtext""  
    ng-model-options="{debounce: {'default': 1000, 'blur': 0}}"
    class="form-control" 
    placeholder="Search" >

Now we can set ng-model-options debounce with time and when blur, model need to be changed immediately otherwise on save it will have older value if delay is not completed.


For those who uses keyup/keydown in the HTML markup. This doesn't uses watch.

JS

app.controller('SearchCtrl', function ($scope, $http, $timeout) {
  var promise = '';
  $scope.search = function() {
    if(promise){
      $timeout.cancel(promise);
    }
    promise = $timeout(function() {
    //ajax call goes here..
    },2000);
  };
});

HTML

<input type="search" autocomplete="off" ng-model="keywords" ng-keyup="search()" placeholder="Search...">

Debounced / throttled model updates for angularjs : http://jsfiddle.net/lgersman/vPsGb/3/

In your case there is nothing more to do than using the directive in the jsfiddle code like this:

<input 
    id="searchText" 
    type="search" 
    placeholder="live search..." 
    ng-model="searchText" 
    ng-ampere-debounce
/>

Its basically a small piece of code consisting of a single angular directive named "ng-ampere-debounce" utilizing http://benalman.com/projects/jquery-throttle-debounce-plugin/ which can be attached to any dom element. The directive reorders the attached event handlers so that it can control when to throttle events.

You can use it for throttling/debouncing * model angular updates * angular event handler ng-[event] * jquery event handlers

Have a look : http://jsfiddle.net/lgersman/vPsGb/3/

The directive will be part of the Orangevolt Ampere framework (https://github.com/lgersman/jquery.orangevolt-ampere).


Just for users redirected here:

As introduced in Angular 1.3 you can use ng-model-options attribute:

<input 
       id="searchText" 
       type="search" 
       placeholder="live search..." 
       ng-model="searchText"
       ng-model-options="{ debounce: 250 }"
/>