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.
Angular $timeout is a wrapper written for window. setTimeout in form of a try catch block which throws exceptions via $exceptionHandler service. $timeout accepts the function to be delayed, delay time, a boolean to invoke $. apply and parameters to be passed to the function.
The '$timeout' service of AngularJS is functionally similar to the 'window. setTimeout' object of vanilla JavaScript. This service allows the developer to set some time delay before the execution of the function.
Definition and UsageThe ng-change event is triggered at every change in the value. It will not wait until all changes are made, or when the input field loses focus. The ng-change event is only triggered if there is a actual change in the input value, and not if the change was made from a JavaScript.
The ng-model-options directive is used to control the binding of an HTML form element and a variable in the scope. You can specify that the binding should wait for a specific event to occur, or wait a specific number of milliseconds, and more, see the legal values listed in the parameter values below.
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>
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With