I am going to explore the Angular digest cycle
, which is the process behind Angular data binding
. By the end of this post, you’ll understand how data binding works in angular, what is digest cycle, how we trigger digest cycle by calling $scope.$apply()
and how exactly digest cycle works.
First of all, let me tell you one thing about Javascript .i.e
Javascript Is Turn Based.
The JavaScript code we write doesn’t all run in one go, instead it executes in turns
. Each of these turns runs uninterrupted from start to finish, and when a turn is running, nothing else happens in our browser.
Instead, whenever there is a task that takes some amount of time, such as an Ajax request
, waiting for a click event
, or setting a timeout
, we set up a callback function and finish our current turn. Later, when the Ajax request completes, a click is detected, or the timer completes, a new JavaScript turn is created and the callback is run to completion.
Let’s look at the below code
1 2 3 4 5 6 7 |
var button = document.getElementById('clickMe'); function buttonClicked () { alert('the button was clicked'); } button.addEventListener('click', buttonClicked); |
When the JavaScript code is loaded, that is a single turn. It finds a button, adds a click listener, and sets a timeout. Then the turn is complete, and the browser will update the web page if necessary, and begin accepting user input.
If the browser detects a click on #clickMe
, it creates a new turn, which executes the buttonClicked
function. When that function returns, that turn is complete.
Now take a look at a very simple AngularJS snippet where you start typing your name into an input field. There is also a div that shows what you typed, in real time:
1 2 |
<input id="input" type="text" ng-model="name"/> <div id="output">{{name}}</div> |
Whenever the name model
changes, the expression automatically updates itself. It’s done just by setting up a watcher
on the name model.
Most watchExpressions
are created implicitly within AngularJS, but you can also create them manually. Let me show you how to do that:
1 |
scope.$watch('name', function(newValue, oldValue) { /* Do something clever here */ }); |
Note : Here in watch expression we pass newValue and oldValue. When watch expression changes, then the listener function is called. This is also known as dirty checking
.
For this strategy to work, we need to know when data has possibly changed, and this is where $scope.$apply
comes into play.
The step that checks to see if any binding values have changed actually has a method, $scope.$digest()
. But we almost never call it directly, instead we use $scope.$apply()
which will call $scope.$digest()
for you.
$scope.$apply()
takes a function or an Angular expression string, and executes it, then calls $scope.$digest()
to update any bindings or watchers
.You can also say that it just starts new javascript turn. Digest cycle executes at least twice
.
So, when do you need to call $apply()? Very rarely, actually. AngularJS actually calls almost all of your code within an $apply call
. Events like ng-click
, controller initialization
, $http callbacks
are all wrapped in $scope.$apply()
. So you don’t need to call it yourself, in fact you can’t. Calling $apply inside $apply will throw an error.
So what happens when we write
1 |
<input type="text" ng-model="name"/> |
1. The directive ng-model registers a keydown listener
with the input field. When the input field text changes a keydown event is fired and the corresponding listener is called to handle it.
2. Inside the keydown listener the directive assigns the new value of input field to the scope model specified in ng-model. In our case the model name is updated with the new value. This code is wrapped inside $apply() call.
3. After $apply() ends the digest cycle starts in which the watchers are called. If you have an expression {{name}}
in the view it has already set up a watcher on scope model name. So, in digest cycle this watcher gets called and the listener function executes with the newValue and oldValue as arguments. The job of this listener is to update the DOM
with the newValue probably using innerHTML
.
4. The overall result is that you see the {{name}} updated with whatever you type into the input field instantly.
References:
http://www.thinkful.com/projects/understanding-the-digest-cycle-528/