Reputation: 730
I have applied ng-model-options
on input
with following configuration
ng-model-options="{updateOn:'default blur',debounce:{default:1000,blur:0}}"
And as per the applied configuration to the ng-model-options
I am expecting the updated ng-model's
value on ng-blur
event but it doesn't return the new value despite having set 0
debounce value for blur event.
*Note: This problem is occurred only if user focused out before the time given in the default
debounce i.e. 1000
HTML:
<input type="text" ng-model="myname" ng-blur="onBlur(myname)" ng-model-options="{updateOn:'default blur',debounce:{default:1000,blur:0}}">
<input type="text" ng-model="output"/>
JS:
$scope.myname = "Yogesh";
$scope.output = "";
$scope.onBlur = function(a){
$scope.output = a;
}
Plunker link: https://embed.plnkr.co/XJMUUD/
Why debounce is not working? correct me if I am doing wrong any!
Thanks in advance :)
I also have given the answer to my question! let me know how it is flexible to use and how it will help to reduce event digest cycles.
Upvotes: 2
Views: 5512
Reputation: 6620
How ng-model-options will help to reduce event digest cycles?
Yes, ng-model-options
can help you limit the number of $digest
cycles. If you were to use just ng-model without setting any options for it, then your $digest cycle will run for each change in the value of ng-model. If $digest
cycle is packed full of data to be dirty-checked, the user is going to see lag in the UI whilst (for instance) typing inside an .Here is an example referenced from toddmotto's blog.
// app.js
angular
.module('app', []);
function trackDigests($rootScope) {
function link($scope, $element, $attrs) {
var count = 0;
function countDigests(newValue, oldValue) {
count++;
$element[0].innerHTML = '$digests: ' + count;
}
$rootScope.$watch(countDigests);
}
return {
restrict: 'EA',
link: link
};
}
angular
.module('app')
.directive('trackDigests', trackDigests);
<script src="//code.angularjs.org/1.4.7/angular.min.js"></script>
<div ng-app="app">
<div>
<form name="myForm">
<h3>Standard <input></h3>
<track-digests></track-digests>
<input
type="text"
name="test"
ng-model="test">
</form>
</div>
</div>
As you can see from our standard input, $digest
cycle is getting triggered for each character we type in the input field. This may cause end user delay for large applications.
Now we will see the case for inputs with ng-model options.
// app.js
angular
.module('app', []);
function trackDigests($rootScope) {
function link($scope, $element, $attrs) {
var count = 0;
function countDigests(newValue, oldValue) {
count++;
$element[0].innerHTML = '$digests: ' + count;
}
$rootScope.$watch(countDigests);
}
return {
restrict: 'EA',
link: link
};
}
angular
.module('app')
.directive('trackDigests', trackDigests);
<script src="//code.angularjs.org/1.4.7/angular.min.js"></script>
<div ng-app="app">
<div>
<form name="myForm">
<h3>ngModelOptions <input></h3>
<track-digests></track-digests>
<input
type="text"
name="test"
ng-model="test"
ng-model-options="{
updateOn: 'blur'
}">
</form>
</div>
</div>
Here we can see that the $digest
cycle is getting triggered only when we lose focus from the input. So, basically the ngModelOptions
are giving us control over how and when $digest
cycles occur.
Let us take even more control over the $digest
cycle by introducing debounce so that we can tell angular when to update.
// app.js
angular
.module('app', []);
function trackDigests($rootScope) {
function link($scope, $element, $attrs) {
var count = 0;
function countDigests(newValue, oldValue) {
count++;
$element[0].innerHTML = '$digests: ' + count;
}
$rootScope.$watch(countDigests);
}
return {
restrict: 'EA',
link: link
};
}
angular
.module('app')
.directive('trackDigests', trackDigests);
<script src="//code.angularjs.org/1.4.7/angular.min.js"></script>
<div ng-app="app">
<div>
<form name="myForm">
<h3>ngModelOptions <input></h3>
<track-digests></track-digests>
<input
type="text"
name="test"
ng-model="test"
ng-model-options="{
updateOn: 'default blur',
debounce: {
'default': 250,
'blur': 0
}
}">
</form>
</div>
</div>
The above illustrates that default will be updated 250ms after the event stops, and blur will update immediately as the user leaves the input (if this is the desired behaviour we want).
Start typing again, then stop and note the $digest
count is severely lower than the initial demonstration. You can then click/tab out the to call another $digest
immediately.
Default and change in debounce objects property are nothing but events. Default is not a DOM event, this is simply part of the ng-model-options api. Suppose you are setting your ngModelOptions like
ng-model-options="{
updateOn: 'default'
}"
Then there will be no change in the behaviour of your input field from default behaviour. This configuration is not really useful until we combine it with debounce like
ng-model-options="{
updateOn: 'default',
debounce: { 'default': 500 }
}"
This will make the input to update after 500ms. So basically this answers what default is. You can use other DOM events like change,blur,mouseover
...etc for your debounce.
When you used ng-model-options="{updateOn:'default blur',debounce:{default:1000,blur:0}}"
, the ng-blur was getting triggered with the old value of ng-model and after that only the updateOn events were fired.So basically the output will contain the old value of ng-model though the myname will be updated.
Working Example: https://plnkr.co/edit/2JPHXvXd992JJ0s37YC9?p=preview
Now, when you used ng-model-options="{updateOn:'default change blur',debounce:{default:1000,blur:0,change:0}}"
,the ng-blur was getting triggered with the new value of ng-model because setting change:0 made the updateOn events to fire before ng-blur.So basically the output was updated with the new value of ng-model along with myname.
Working Example : https://plnkr.co/edit/9wdA0he2YVcsPRLJ1Ant?p=preview
Upvotes: 9
Reputation: 2678
This is because when you set debounce
the digest loop is triggered after the given time. After the digest loop is triggered It checks whether a value has changed that hasn’t yet been synchronized across the app.
In your case the input value will be synchronized with the model variable myname
after 1000ms or 1s but immediate update when removing the focus. Your method onBlur(myname)
is called with the previous value of myname
, because at the time function was called it still has the previous value of the argument passed to it (it can't update the value of myname and call the function at same time) and after that the digest loop update myname
.
You may check that the model is update immediatly by putting {{myname}}
next to the inputs.
ng-blur
-> call onBlur(myname)
-> here myname is with old value still
-> trigger digest loop (here is where the new value is assigned to myname)
-> update model & view
{updateOn: 'event'} specifies that the binding should happen when the specific event occur.
To update the model before your element lose focus (onblur) you have to use updateOn: change
and set its time to 0, that's how on each change angular will immediatly bind the new value to your function param.
Upvotes: 1
Reputation: 730
After some research we came across this configuration
ng-model-options="{updateOn:'default change blur',debounce:{default:1000,blur:0,change:0}}"
Which works fine! as expected on ng-blur
event it returns updated value.
Upvotes: 2