Thursday 25 July 2013

Behaviour of Scope in Angular JS Directives

Directive is the coolest and most crucial feature provided by Angular JS. Use of scope in directives makes the process of writing a directive challenging. Behaviour of scope depends on the way it is assigned in the directive. Following are different scenarios of using scope inside directive:

  • Scope not assigned or set to false
  • Scope set to true
  • Isolated scope

Following is a simple controller with just one value assigned to scope that we will be using in a sample page:
var app = angular.module("myApp", []);

app.controller("SampleCtrl", ['$scope', function ($scope) {
    $scope.val = 0;
}]);


Let’s have a look at each of the above scenarios individually.

Scope not assigned or set to false:
Consider the following directive:
app.directive("dirWithoutScope", function () {
    return{
        scope:false,
        link: function (scope, elem, attrs, ctrl) {
            scope.val=20;
        }
    }
});

As the directive is not making any changes, scope of this directive remains same as the scope of its parent element. The statement in the link function modifies value of the property val, which is set in the controller. Both of the following span elements will display same value, which is 20.
<span>{{val}}</span>

<span dir-without-scope>{{val}}</span>


Scope set to true:
In this case, scope of the directive would be prototypically inherited from scope of its parent element. Let’s consider the following directive:

app.directive("dirWithScopeTrue", function () {
    return{
        scope:true
    }
});

As the scope is prototypically inherited, properties of the parent scope are accessible using this scope. The following span tag will still hold the value assigned to the parent scope:
<span dir-with-scope-true>{{val}}</span>

Let’s complicate the scenario a bit by adding the following link function to the above directive:
link: function (scope, elem, attrs, ctrl) {
    scope.val=25;
}

The value 25 isn’t assigned to the val property in the parent scope. Instead, this statement creates a new property in the child scope and assigns the value to it. The span element created above will hold the value 25 now.

If the value is set through $parent property of the scope, the value gets assigned to the parent scope.

$scope.$parent.val=25;

If statement of link function is replaced with the above statement, values in all span elements will change to 25.

Isolated scope:
If a new scope is created in the directive, the scope doesn’t inherit from the parent scope anymore. Properties of the parent scope are not accessible with the new scope. Following directive contains an isolated scope:

app.directive("dirWithBlankScope", function(){
    return{
        scope:{
        },
        link:function(scope, elem, attrs, ctrl){
            scope.val=30;
        }
    }
});

The assignment statement in the link function creates a property val in the new scope. It doesn’t alter the value of the property assigned in the controller or the previous directive. Following span prints value of the new property:
<span dir-with-new-scope>{{val}}</span>

Properties of the scope of parent element are still accessible through $parent property of the scope.
<span dir-with-new-scope>{{$parent.val}}</span>



Happy coding!

4 comments:

  1. very nice!! I got quite confused between the different possible values "true", "false" and {}. Your explanations have made it quite clear what does each one.
    Cheers.

    ReplyDelete
  2. Sometimes angular documentation is too technical and not enough pragmatic, you've nailed it.
    Think about submitting a pull request :-)

    ReplyDelete
  3. It should perhaps be mentioned that using $parent property is not a recommended practice as it breaks the encapsulation created by isolated scope. A better way is to declare what is used inside the 'scope={}' declaration.

    ReplyDelete

Note: only a member of this blog may post a comment.