AngularJS


digest loop walkthrough All Versions

0.9.0
0.9.1
0.9.2
0.9.3
0.9.4
0.9.5
0.9.6
0.9.7
0.9.8
0.9.9
0.9.10
0.9.11
0.9.12
0.9.13
0.9.14
0.9.15
0.9.16
0.9.17
0.9.18
0.9.19
0.10.0
0.10.1
0.10.2
0.10.3
0.10.4
0.10.5
0.10.6
1.0.0
1.0.1
1.0.2
1.1.0
1.0.3
1.1.1
1.0.4
1.1.2
1.0.5
1.1.3
1.1.4
1.0.6
1.0.7
1.1.5
1.0.8
1.2.0
1.2.1
1.2.2
1.2.3
1.2.4
1.2.5
1.2.6
1.2.7
1.2.8
1.2.9
1.2.10
1.2.11
1.2.12
1.2.13
1.2.14
1.2.15
1.2.16
1.2.17
1.2.18
1.2.19
1.2.20
1.2.21
1.2.22
1.2.23
1.2.24
1.2.25
1.2.26
1.3.0
1.3.1
1.3.2
1.3.3
1.2.27
1.3.4
1.3.5
1.3.6
1.2.28
1.3.7
1.3.8
1.3.9
1.3.10
1.3.11
1.3.12
1.3.13
1.3.14
1.3.15
1.4.0
1.3.16
1.4.1
1.3.17
1.4.2
1.4.3
1.4.4
1.3.18
1.4.5
1.3.19
1.4.6
1.2.29
1.3.20
1.4.7
1.4.8
1.4.9
1.5.0
1.4.10
1.5.1
1.5.2
1.5.3
1.5.4
1.5.5
1.5.6
1.5.7
1.5.8

This draft deletes the entire topic.

inline side-by-side expand all collapse all

Examples

  • Improvements requested by fracz, charlietfl:

    • This example does not sufficiently illustrate the point and needs to be edited to provide more details. - jul 24 at 18:12
      Comments:
      • Nothing shown in example would ever merit use of `$apply` - charlietfl
    • This example is not helpful and should be edited or deleted. - jul 28 at 6:58
    2

    Sometimes running the children's $digest is not enough:

    <div>
        {{person.name}}
        <span ng-repeat="number in [1,2,3,4,5]"><input ng-model="person.name" /></span>
    </div>
    

    There are two problems with the current scope

    • User interaction inside span needs to update its parents
    • Some digest loop callbacks could create changes which wont be updated in the DOM until another digest loop starts

    Solving the first problem is done by keeping a reference to the root scope, and on user interaction - calling $digest of the $rootScope. That is what the $apply function dose:

    //create new child scope
    $scope.prototype.$new = function(){
        var nScope = new $scope();
        nScope.parent = this;
        nScope.$root = this.$root || this;
        this.$children.push(nScope);
        return nScope;
    }
    
    $scope.prototype.$apply = function(){
        this.$root.$digest();
    }
    

    The second problem is solved by keeping track of the scope drityness after each digest loop

  • 2

    Implementing two-way-data-binding, to achieve the result from the previous example, could be done with two core functions:

    • $digest is called after a user interaction (binding DOM=>variable)
    • $watch sets a callback to be called after variable changes (binding variable=>DOM)

    note: this is example is a demonstration, not the actual angular code


    <input id="input"/>
    <span id="span"></span>
    

    The two functions we need:

    var $watches = [];
    function $digest(){
        $watches.forEach(function($w){
            var val = $w.val();
            if($w.prevVal !== val){
                $w.callback(val, $w.prevVal);
              $w.prevVal = val;
            }
        })
    }
    function $watch(val, callback){
        $watches.push({val:val, callback:callback, prevVal: val() })
    }
    

    Now we could now use these functions to hook up a variable to the DOM (angular comes with built-in directives which will do this for you):

    var realVar;    
    //this is usually done by ng-model directive
    input1.addEventListener('keyup',function(e){
        realVar=e.target.value; 
        $digest()
    }, true);
    
    //this is usually done with {{expressions}} or ng-bind directive
    $watch(function(){return realVar},function(val){
        span1.innerHTML = val;
    });
    

    Off-course, the real implementations are more complex, and support parameters such as which element to bind to, and what variable to use

    A running example could be found here: https://jsfiddle.net/azofxd4j/

  • 2

    The previous example is good enough when wee need to bind a single html element, to a single variable.

    In reality - we need to bind many elements to many variables:

    <span ng-repeat="number in [1,2,3,4,5]">{{number}}</span>
    

    This ng-repeat binds 5 elements to 5 variables called number, with a different value for each of them!


    The way angular achieves this behavior, is using a separate context for each element which needs separate variables. This context is called a scope.

    Each scope contains properties, which are the variables bound to the DOM, and the $digest and $watch functions are implemented as methods of the scope.

    The DOM is a tree, and variables need to be used in different levels of the tree:

    <div>
        <input ng-model="person.name" />
        <span ng-repeat="number in [1,2,3,4,5]">{{number}} {{person.name}}</span>
    </div>
    

    But as we saw, the context(or scope) of variables inside ng-repeate is difrrent then the context above it. To solve this - angular implements scopes as a tree.

    Each scope, has an array of children, and calling its $digest method will run all of its children's $digest method.

    This way - after changing the input - $digest is called for the div's scope, which then runs the $digest for its 5 children - witch will updates their contents


    A simple implementation for a scope, could look like this:

    function $scope(){
        this.$children = [];
        this.$watches = [];
    }
    
    $scope.prototype.$digest = function(){
        this.$watches.forEach(function($w){
            var val = $w.val();
            if($w.prevVal !== val){
                $w.callback(val, $w.prevVal);
              $w.prevVal = val;
            }
        });
        this.$children.forEach(function(c){
            c.$digest();
        });
    }
    
    $scope.prototype.$watch = function(val, callback){
        this.$watches.push({val:val, callback:callback, prevVal: val() })
    }
    

    note: this is example is a demonstration, not the actual angular code

I am downvoting this example because it is...

Syntax

  • $scope.$watch(watchExpression, callback, [deep compare])
  • $scope.$digest()
  • $scope.$apply([exp])

Parameters

Parameters

Remarks

Remarks

Still have question about digest loop walkthrough? Ask Question

$apply and $digest dirtyness

2

Sometimes running the children's $digest is not enough:

<div>
    {{person.name}}
    <span ng-repeat="number in [1,2,3,4,5]"><input ng-model="person.name" /></span>
</div>

There are two problems with the current scope

  • User interaction inside span needs to update its parents
  • Some digest loop callbacks could create changes which wont be updated in the DOM until another digest loop starts

Solving the first problem is done by keeping a reference to the root scope, and on user interaction - calling $digest of the $rootScope. That is what the $apply function dose:

//create new child scope
$scope.prototype.$new = function(){
    var nScope = new $scope();
    nScope.parent = this;
    nScope.$root = this.$root || this;
    this.$children.push(nScope);
    return nScope;
}

$scope.prototype.$apply = function(){
    this.$root.$digest();
}

The second problem is solved by keeping track of the scope drityness after each digest loop

$digest and $watch

2

Implementing two-way-data-binding, to achieve the result from the previous example, could be done with two core functions:

  • $digest is called after a user interaction (binding DOM=>variable)
  • $watch sets a callback to be called after variable changes (binding variable=>DOM)

note: this is example is a demonstration, not the actual angular code


<input id="input"/>
<span id="span"></span>

The two functions we need:

var $watches = [];
function $digest(){
    $watches.forEach(function($w){
        var val = $w.val();
        if($w.prevVal !== val){
            $w.callback(val, $w.prevVal);
          $w.prevVal = val;
        }
    })
}
function $watch(val, callback){
    $watches.push({val:val, callback:callback, prevVal: val() })
}

Now we could now use these functions to hook up a variable to the DOM (angular comes with built-in directives which will do this for you):

var realVar;    
//this is usually done by ng-model directive
input1.addEventListener('keyup',function(e){
    realVar=e.target.value; 
    $digest()
}, true);

//this is usually done with {{expressions}} or ng-bind directive
$watch(function(){return realVar},function(val){
    span1.innerHTML = val;
});

Off-course, the real implementations are more complex, and support parameters such as which element to bind to, and what variable to use

A running example could be found here: https://jsfiddle.net/azofxd4j/

the $scope tree

2

The previous example is good enough when wee need to bind a single html element, to a single variable.

In reality - we need to bind many elements to many variables:

<span ng-repeat="number in [1,2,3,4,5]">{{number}}</span>

This ng-repeat binds 5 elements to 5 variables called number, with a different value for each of them!


The way angular achieves this behavior, is using a separate context for each element which needs separate variables. This context is called a scope.

Each scope contains properties, which are the variables bound to the DOM, and the $digest and $watch functions are implemented as methods of the scope.

The DOM is a tree, and variables need to be used in different levels of the tree:

<div>
    <input ng-model="person.name" />
    <span ng-repeat="number in [1,2,3,4,5]">{{number}} {{person.name}}</span>
</div>

But as we saw, the context(or scope) of variables inside ng-repeate is difrrent then the context above it. To solve this - angular implements scopes as a tree.

Each scope, has an array of children, and calling its $digest method will run all of its children's $digest method.

This way - after changing the input - $digest is called for the div's scope, which then runs the $digest for its 5 children - witch will updates their contents


A simple implementation for a scope, could look like this:

function $scope(){
    this.$children = [];
    this.$watches = [];
}

$scope.prototype.$digest = function(){
    this.$watches.forEach(function($w){
        var val = $w.val();
        if($w.prevVal !== val){
            $w.callback(val, $w.prevVal);
          $w.prevVal = val;
        }
    });
    this.$children.forEach(function(c){
        c.$digest();
    });
}

$scope.prototype.$watch = function(val, callback){
    this.$watches.push({val:val, callback:callback, prevVal: val() })
}

note: this is example is a demonstration, not the actual angular code

two way data binding

0

Angular has some magic under its hood. it enables binding DOM to real js variables.

Angular uses a loop, named the "digest loop", which is called after any change of a variable - calling callbacks which update the DOM.

For example, the ng-model directive attaches a keyup eventListener to this input:

<input ng-model="variable" />

Every time the keyup event fires, the digest loop starts.

At some point, the digest loop iterates over a callback which updates the contents of this span:

<span>{{variable}}</span>

The basic life-cycle of this example, summarizes (very Schematically) how angular works::

  1. Angular scans html
    • ng-model directive creates a keyup listener on input
    • expression inside span adds a callback to digest cycle
  2. User interacts with input
    • keyup listener starts digest cycle
    • digest cycle calles the callback
    • Callback updates span's contents

Topic Outline