Scopes in directives of AngularJS

For this blog , you need a basic understanding about custom directives in AngularJS .

Scope is a concept of AngularJS which can be pretty tricky at times, and can get difficult to make sense out of it . Thanks, to some extensions, like batarang , we can check the scope IDs of elements and check which have same or different scope.   All DOM elements that have been compiled by AngularJS have a scope associated  with them. In most cases DOM elements do not have scopes defined directly on them but get their scope from some ancestor element. New scopes are created by directives that specify a scope property on their directive definition object( will talk about that in a short while) .  If we talk about built in directives of AngularJS, then only a few core directives define new  scopes, these are ng-controller, ng-repeat, ng-include, ng-view, and ng-switch. They all create child scopes that prototypically inherit  from their parent scopes.
And when we create a custom directive using app.directive  , which uses a  service called $compile and its provider  $compileProvider , we also create a scope(or not) based on our requirements. If you are wondering what this $compile is, $compile is at the heart of AngularJS machinery and can turn HTML strings (with directives and interpolation expressions) into live DOM.  We can add our own functionality to this compilation process using the compile property like ::

app.directive('myDirective',function(){
return{
compile: function(element,attrs){
// here we can add functionality to be run during the compile phase
}
}
})

I will tell in detail about the compile (and about creating a custom directive as a whole) in a later blog.

Now, getting on with the scope thing, the directive service has a scope property, which defines the scope of the directive which we are creating ..
There are, three options for the scope directive ::

app.directive('myDirective',function(){
return{
scope: true //or scope: false //or scope:{}
}
});

scope:false is the default value. Now, what does  these actually mean . 
Put in simple words, scope:false , means that you dont want a separate scope for this directive. It is also called 'shared scope' . Shared scope simply means that the directive works with the same scope that is already available for the DOM node where the directive appears without creating a new one. 

scope: true,  means 'inherited scope' , 
In this case, a  new scope is created for our directive, but it inherits all the properties of the parent scope through JavaScript's prototypical inheritance: when the directive accesses a property on it's new scope, the property is first searched on the current scope and if it's found then it's value is returned, otherwise, if it's not found, the same property name is searched in the parent scope and that value is returned (the search traverses all the parents until the property is found or until there are no more parents); if a value is assigned to a property on the new directive's scope and the same property already exists in the parent, accessing the property value directly in the new scope will in fact override the parent's property value and any change to the property in the parent will not be propagated anymore to the child scope.

and, the one used most commonly is  scope:{ }, which means isolated scope .
As its name suggests, it is an isolated scope, which has no connection with the parent element in which it resides, though it can access the parent element by $parent, but its functionality is not affected by the scope of the parent in which it resides.

We can however have connection between the isolated scope, and the parent scope in terms of accessing data or methods or enabling data binding. There are three types of interface we can specify
between the element's attributes and the isolated scope: interpolate (@) , data bind (=) , and expression (&) .

The various options available with the scope are ::
scope:{
key1:'@attribute1',
key2:'=attribute2',
key3:'&attribute3'
}

These are written in form of key: value pairs .The key is the name of the field on the isolated scope. The value is one of @ , = , or & followed by the name of the attribute on the element. If you omit the attribute names::

scope:{
key1:'@',
key2:'=',
key3:'&'
}
then key1, key2, key3 will be taken as the attribute names.. Now, i am having the temptation to blurt out the whole directives concept here, but i will refrain myself :P and talk about the directives(along with the scope options above) in a separate blog, to make this blog to the point :) .

Ok, so  let me give examples of all these scope variations in terms of scope ids::

Shared Scope:::

Parent Scope Id: 002
Directive Scope Id: 002
(they are bound to the same scope)

Inherited Scope::

Parent Scope Id : 002
Directive Scope Id : 003( but is inherited from the parent scope)

Isolated Scope::

Parent Scope Id: 002
Directive Scope Id : 004(is completely isolated from the parent scope)


Now, what if multiple directives with different scope variations appear on a single HTML DOM node.
The way they interact with each other changes a little bit in these cases..


   1) If there are only  shared scope directives, then they will behave as usual, and share the parent scope.

  2) If there are only inherited scope directives, then they will all be bound to a single scope inherited from the parent scope.

  3) If there are mix of shared and inherited scopes, then the shared scope will also behave as inherited scope , and all these directives will be bound to a single inherited scope .

  4) There can only be a single isolated scope directive on a DOM node .

  5) If you mix one isolated scope directive to any bunch of directives mentioned in 1)  2) 3) , then others will behave as mentioned in these points, along with one isolated scope created for this  isolated scope directive.

So, this is all i had to talk about scopes . Thanks, for giving it a read and do add additional info on these :)

2 comments: