AngularJS $watch() ,$digest() and $apply() – User friendly Tech help
This post is meant for novice AngularJS programmers to enlighten them with basic comprehension of how data-binding works. To deep-dive into data-binding, you need a clear vision of how $watch(), $digest(), $apply() and dirty checking works.Lets go step by step on this topic.
n
nDo folow us on FB page, Twitter or G+ for more updates and learning.
n
n“It is not about doing so much as understanding the process itself”
n
nThe browser behavior on events:
nGenerally, browser waits for user interactions, eg: button click event or change events. If you click on a button or select a checkbox, the event’s callback function will run inside JavaScript where all the DOM manipulations will be completed. Browser will get notification to render appropriate changes in the DOM after the callback is completed.
n
nAngular extends this events-loop into a vital concept known as angular-context. To explain what this context is and how it works we will need to explain more concepts.
n
n$watch
nWhen you create a data-binding in your view, AngularJS internally creates a $watch in the $watch list . It means it will detect the changes in the modal it is watching.
nEg:
n
Here we are having $scope.
ncustomerName which is bound to the first input and $scope.customerId which is bound to second input.
n
nHence, we have created two $watch in the $watch list.
n
n
nGuess how many $watch are created here?
n2 –Wrong(For customer name and id)
n3-Right (2 for customer name,id and 1 for ng-repeat)
n
So if we have 5 customers in a list, the $watch count will be 1+(2*5) i.e. 11
nSo all the bindings in UI creates a $watch.
n
nBut now the question arises is when $watch created for ng-repeat or other directives are?
nWhen template has loaded, the compiler starts looking for every directive and subsequently creates required $watch.
n
n$digest (Extended event loop)
nThe $digest loop will get invoked when the browser receives an event which can be managed by angular context. This comprises of two inner loops. One process the $evalAsync queue while the other keeps an eye on $watch list of the current scope and its children checking following process:
n
nHey $watch, what is your value?
nI am “Selenium”.
nIs it changed?
nNo, Boss.
nNo action will perform on this, it will iterate to the next.
nBuddy, what is your value?
n It is “User Friendly Techy Help”.
nIs it changed?
nYes, It was “Unified Functional Testing”.
nGreat, now we have to update the DOM.
n
The $digest() keeps calling the all the watchers that were being created and then make changes in the DOM.
nThis process is called as dirty-checking.
n
nKey Points:
n
- n
- If any one of them has changed, the loop will run again until all of the $watch, report no changes to ensure that the model is clean.
- This function will throw ‘Maximum iteration limit exceeded.’ if the number of iterations exceeds 10.
- Usually, $digest is never called directly, instead we use $apply() which will force a $digest().
- The significant point is that “All the events” that enters the angular context will fire a $digest loop. Eg:
n
n
n
n
In this example, we have only two $watch as ng-click doesn’t create any $watch(the function will not change).
n
nWho notifies which event enters the angular context and which one do not?
nAnswer is: $apply
nWhenever an event is fired, it will go through the angular-context and fire $apply(), else it will run outside it. In earlier example: You may have noticed that it works without calling $apply(). The reason behind this is Angular called the $apply method internally.
n
nSo, when do you need to call $apply()?
nThere are some corner cases where AngularJS does not call the $digest() function for you. You will usually observe that by checking that the data bindings do not update the displayed values.Actually, the events never enter the angular context and the $digest loop is never fired. Be sure you call, $apply() if you need to run a $digest loop to update your DOM.
n
nKey Points:
n
- n
- AngularJS calls almost all of your code within an $apply call. So you need not to call it explicitly.
- Calling $apply() inside $apply() will throw an error.
- You need to call it only if you code is not written using methods from AngularJS library. Ex: if you are binding a double-click event on an element using jQuery, then you have to wrap your code in $apply().If you write any code that uses Ajax without $http, or listens for events without using Angular’s ng-* listeners, or sets a timeout without $timeout, you should wrap your code in $apply().
- Some people don’t recommend calling $apply because they think that they are doing something wrong. That is not true. It is just Angular that doesn’t get any notification when a 3rd party library wants to update the bindings.
n
n
n
n
n
Sample:
nWorking Demo
n
nHTML:-
nJavaScript :-
n
JavaScript Code |
n
n Output:-
n
nTry to execute the code and observe how output varies, it is changing from “Title” ->”UFT”->”User Friendly Techy Help”
n
Scope’s $apply() method transitions through the following stages:
n1.The expression is executed using the $eval() method.
n2.Any exceptions from the execution of the expression are forwarded to the $exceptionHandler service.
n3.The watch listeners are fired immediately after the expression was executed using the $digest() method.
n
nIf you want to create your own $watch, checkout this tutorial.
n
nWe hope that you understand how data-binding works in Angular. Don’t assume that dirty-checking is slow. It is fast as lightening. It can be laggy, if you have 2k-3k $watch in your template. In ECMAScript 7, we will have Object.observe which will improve the $digest loop.
nIf you find that we have missed something or there is some improvements or any thing You want to share please feel free to add Your comments or contact us.
n
nCreate movable tooltip in AngularJS
n
n