Michael
Michael

Reputation: 7377

AngularJS - Fire $timeout event on ng-change only once

I have an ng-change on an input field in html that's bound to a scope variable.

<input type="text" ng-model="test" ng-change="change()" required>

var change = function(){ redraw_graph()}

Now when I change the input box, it redraws the graph for every new character I write. I want to have a delay (N seconds), so angular will wait before the user is done typing before the ng-change event fires. And if there are multiple ng-change events fired, it cancels the earlier ones and only executes the latest ones.

I've incorporated the delay with a timeout, but after N seconds the ng-change event still fires more than once. I've solved this problem before, but I can't figure out how to do it currently.

Upvotes: 7

Views: 4018

Answers (3)

Rohan B&#252;chner
Rohan B&#252;chner

Reputation: 5393

To me it seems like what you're asking for is already built into AngularJS. Thus, if you make use of the the ngModelOptions directive you can use the debounce property:

ng-model-options="{ debounce: 1000 }"

To quote the docs

.."/or a debouncing delay so that the actual update only takes place when a timer expires; this timer will be reset after another change takes place."


Working sample

  angular.module('optionsExample', [])
    .controller('ExampleController', ['$scope',
      function($scope) {
        $scope.user = {
          name: 'say'
        };
      }
  ]);
<!doctype html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Example - example-ngModelOptions-directive-debounce-production</title>
  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.0-rc.5/angular.min.js"></script>
  <script src="app.js"></script>
</head>

<body ng-app="optionsExample">
  <div ng-controller="ExampleController">
    <form name="userForm">
      Name:
      <input type="text" 
             name="userName" 
             ng-model="user.name" 
             ng-model-options="{ debounce: 1000 }" />
      <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button>
      <br />
    </form>
    <pre>user.name = <span ng-bind="user.name"></span></pre>
  </div>
</body>

</html>

Upvotes: 11

Noah Solomon
Noah Solomon

Reputation: 1261

You could possibly check out UnderscoreJS, which has .debounce() and .throttle()

Upvotes: 0

Angad
Angad

Reputation: 3429

Based on @Blackhole's suggestion, you can work this out by cancelling your original $timeout.

Here is how you would do it:

var timer;

$scope.change = function(){
   $timeout.cancel( timer );

   timer = $timeout(function() {
             redraw_graph()
           },2000);
 }

Check below plunker to see how it works. An alert box (only one) will popup 2 seconds after you are done with all your changes on the input field. That is, if you change the input field before 2 seconds, you delay the popup by another 2 seconds.

http://plnkr.co/edit/v08RYwCDVtymNrgs48QZ?p=preview

EDIT
While above is one method of doing it, AngularJS has come up with its own implementation for this particular feature in v1.3+. ngModelOptions can be used.

Upvotes: 2

Related Questions