AngularJS: Understanding Directive Scope

In the previous post, we created custom AngularJS directives. However, we did not address how directives manipulate data objects that are defined in the Angular app. In certain use cases, the directive might need to use the objects and functions defined in the controller—manipulate their values and make calls to the functions. In this post we shall explore how this is done from within a directive.

Scope in a Directive

How does a directive interact with the page controller? How can objects be passed to the directive? How are functions defined in scope of the controller called from inside the directive?

Well, all of the directives have a scope associated with them. This scope object is used for accessing the variables and functions defined in the AngularJS controllers, and the controller and link functions of the directive. By default, directives do not create their own scope; instead they use the scope of their parent, generally a controller (within the scope of which the directive is defined).

We can change the default scope of the directive using the scope field of the DDO (Data Definition Object). Note that scope is a property of the directive, just like restrict, template, etc. The value of the scope field defines how the scope of the directive will be created and used. These values are ‘true’, ‘false,’ and ‘{}’.

Let’s start exploring the usage of each of these.

scope: false

This is the default value of scope in the directive definition. In this case, the directive has the same scope as its parent controller. The following is an example of creating a movie directive:

We create a controller and define a movie title in it. This movie will then be modified in the directive.

  • Creating an Angular app and controller
    var movieApp = angular.module("movieApp",[]);
    
    movieApp.controller("movieController",function($scope){
        $scope.movie = "Ice Age";
    });
  • Defining the directive
    movieApp.directive("movieDirective", function(){
        return {
            restrict: "E",
            scope: false,
            template: "<div>Movie title : {{movie}}</div>"+
            "Type a new movie title : <input type='text' ng-model='movie' />"
        };
    });
  • Finally, embedding the directive in HTML
    <div ng-app="movieApp">
        <div ng-controller="movieController">
            <h2>Movie: {{movie}}</h2>
      Change Movie Title : <input type='text' ng-model='movie'/>
            <movie-directive></movie-directive>
        </div>
    </div>

In the code snippet given above, the movie name in the heading tag takes value from the controller’s scope. Note that the movie title in the directive template also gets its value from the controller scope, that is, Ice Age. Since the scope of the directive is not set, it uses the parent scope. Therefore, modifying the movie title in the directive text box modifies the movie title in the heading tag also. Similarly, changes done to the movie title in the heading tag are reflected in the directive. But this restricts the directive to be used outside the controller scope, which reduces the directive’s reusability.

Let’s see the output:

angularjs_part2_1

On changing the Movie Title in the text box above:

angularjs_part2_2

The Movie Title in all of the places gets modified.

scope: true

Let’s change the scope field of the directive in the last example to true

scope: true

In this case,

In this case, the movie title in both the header section and the directive is initialized to the same value defined in the controller’s scope, Ice Age.

Now, modifying the movie title in the directive modifies the directive content only, not the heading content.

In order to see whether changing the heading content changes the directive content, let’s modify the movie title in the text box following the heading tag.

<div ng-app="movieApp">
    <div ng-controller="movieController">
        <h2>Movie: {{movie}}</h2>
	  Change Movie Title : <input type='text' ng-model='movie'/>
        <movie-directive></movie-directive>
    </div>
</div>

Entering the movie title in this text box changes the movie title in the heading tag only and not in the directive.

What happened here? This time the directive got its own scope! Angular creates a new scope for the directive that is inherited from the parent (controller) scope.

angularjs_part2_3

There is one thing to note here: If the content in the text box in the header section is changed first, then this change is also reflected inside the directive. However, after the movie title in the directive is changed, any changes made to the heading text no longer affect the directive content. The explanation for this is that the ng-model creates a new movie variable only after the text box value is changed. Until then, it refers to the value in the parent scope.

angularjs_part2_4

So here the content of both the text boxes change when the header section text box is modified first.

scope: {}

This is how the directive gets its isolated scope. We pass an object for the scope field in this case. This way, the scope of the directive is not inherited from the parent and is instead completely detached from it. Thus, the directive has an isolated scope.

To see it in action, we modify the directive code:

movieApp.directive("movieDirective", function(){
    return {
        restrict: "E",
        scope: {},
        template: "<div>Movie title : {{movie}}</div>"+
        "Type a new movie title : <input type='text' ng-model='movie' />"
    };
});

Note that the directive no longer takes the movie title defined in the controller. It has its own model, ‘movie,’ defined in the template. In addition, the changes to the controller content in no way affect the directive. The directive has complete control over its contents – an isolated scope!

Even though the directive has its own scope, there may be scenarios where the directive needs to exchange data with the parent. AngularJS covers that too, by allowing communication between the parent scope and the directive. The directive can gain access to the parent scope by using some special symbols known as prefixes.

Isolated scope – Prefixes

In the case of an isolated scope, the directive scope is completely unaware of its parent’s scope. So how does a directive access the parent scope objects or make calls to the methods?

The directive scope uses prefixes to achieve that. Using prefixes helps establish a two-way or one-way binding between parent and directive scopes, and also make calls to parent scope methods. To access any data in the parent scope requires passing the data at two places – the directive scope and the directive tag.

  1. Passing data to the directive scope:
    movieApp.directive("movieDirective", function(){
        return {
            restrict: "E",
            scope: {
    		movie: ‘=’
            },
            template: "<div>Movie title : {{movie}}</div>"+
            "Type a new movie title : <input type='text' ng-model='movie' />"
        };
    });
  2. Passing data in the directive tag
    <movie-directive movie="movie"></movie-directive>

Here movie is a property of the directive scope and can be accessed as a directive data. Note that the behavior of these properties defined in the directive scope depends how they are bound to the directive – also known as prefixes – provided. These prefixes are used to bind the parent scope’s methods and properties to the directive scope.

There are 3 types of prefixes in AngularJS:

  1. ‘@’ – Text binding / one-way binding
  2. ‘=’ – Direct model binding / two-way binding
  3. ‘&’ – Behavior binding / Method binding

Let’s continue with the example of the movie directive and add more properties to the scope method so as to understand all of these prefixes.

In this example, we add a rating property to the existing movie directive. Now when the movie is rated, this value is modified in the directive scope and is reflected there only, not in the header scope. However, when the movie name is changed in the movies section, the change is also reflected in the header scope. Finally, a method call displays the movie name in an alert box. The following code puts all of this in action.

  • Modifying the controller
    movieApp.controller("movieController",function($scope){
        $scope.movie = "Ice Age";
        $scope.rating = 5;
        $scope.display = function(movie) {
            alert("Movie : " + movie);
        }
    });
  • Modifying the directive
    movieApp.directive("movieDirective", function(){
        return {
            restrict: "E",
            scope: {
                movie: '=',
                rating: '@',
                display: '&'
            },
            template: "<div>Movie title : {{movie}}</div>"+
            "Type a new movie title : <input type='text' ng-model='movie' />"+
            "<div>Movie rating : {{rating}}</div>"+
            "Rate the movie : <input type='text' ng-model='rating' />"+
            "<div><button ng-click='display(movie)'>View Movie</button></div>"
        };
    });
  • Passing the fields in the directive tag
    <div ng-app="movieApp">
        <div ng-controller="movieController">
            <h2>Movie Name : {{movie}}</h2>
            <h3>Rating : {{rating}}</h3>
            <movie-directive movie="movie" rating="{{rating}}" display="display(movie)">
     </movie-directive>
        </div>
    </div>

And the output:

angularjs_part2_5

After clicking the  View Movie button, we have an alert pop up as:

angularjs_part2_6

Let’s dissect this code. There is a 2-way binding in the movie property. That is, any changes made to the movie title in either the controller or the directive will reflect over onto the other. This is similar to the setting scope: false!

The rating property has a 1-way binding. Thus, changes made in the directive reflect only there. Also note the difference in passing movie and rating in the in the directive tag. Properties that have 1-way binding are enclosed within parenthesis {{}}.

Note that the display function bound in the directive is defined in the controller (parent) scope. Thus, the directive is able to call controller methods.

Directive Functions

In the example given above, though the directive has an isolated scope, it uses controller methods and thus is able to interact with the controller. However, because a directive is an independent unit, it should also have the capability of defining its own methods. For this, the DDO of the directive has a field link. Let’s modify the example above.

  • Modifying the controller to remove the display method from it
    movieApp.controller("movieController",function($scope){
        $scope.movie = "Ice Age";
        $scope.rating = 5;
    });
  • Defining the display method in the link field of the directive
    movieApp.directive("movieDirective", function(){
        return {
            restrict: 'E',
            scope: {
                movie: '=',
                rating: '@',
                display: '&'
            },
            template: "<div>Movie title : {{movie}}</div>"+
            "Type a new movie title : <input type='text' ng-model='movie' />"+
            "<div>Movie rating : {{rating}}</div>"+
            "Rate the movie : <input type='text' ng-model='rating' />"+
            "<div><button ng-click='display(movie)'>View Movie</button></div>",
            
      link: function($scope, element, attrs) {
                $scope.display = function(movie) {
           	    alert("Movie : " + movie);
        	     }
            }
        };
    });

The output is:

angularjs_part2_7

 

After clicking the View Movie button, we have an alert pop up as:

angularjs_part2_8

This output is the same as the previous case, when the display method was defined in the controller.

But why the isolated scope?

Note that we can access the parent scope objects if we set the scope field to true or false. However, since directives are independent components, the best practice would be to pass all of the objects and methods that the directive intends to use in the directive tag itself, and further define them in the DDO.

Implementing functionality in AngularJS becomes more modular and reusable if the code has directives defined. And the application of directives becomes more maintainable and robust if the directive scope is well constructed.

Akanksha Chaturvedi

Akanksha Chaturvedi

Software Engineer

Akanksha Chaturvedi is a Software Engineer at the 3Pillar Noida office in India. She has experience in Java technology, AngularJS, and iOS application development in Swift. She is very passionate about the design and analysis of computer algorithms, and has a keen interest in how those algorithms solve real world problems. She earned a Bachelor of Engineering in Computer Engineering from Netaji Subhas Institute of Technology (NSIT), Delhi. In her leisure time, she loves to travel, play badminton, watch tennis, and read novels.

33 Responses to “AngularJS: Understanding Directive Scope”
  1. Prashant on

    Good Explanation!

    Reply
  2. dancome on

    Hello ~ Awesome content ~ Thank You

    Reply
  3. supriya on

    hi,
    this article seems very effective,thanks for the great article.

    Regards,
    Supriya

    Reply
  4. Sonal Chavan on

    This really helped me… thanks…..

    Reply
  5. Mahesh on

    Hi I am new for angularJs pls give ur email id for any further queries

    Reply
  6. Pinaki on

    Very good doc. Helped me a lot..thanks 🙂

    Reply
  7. Sowmya on

    Hi,
    Nice Article.
    I am interested in learning Angular JS, should I go with Angular 1.x and then port to Angular 2.x or directly to Angular 2?
    Thanks

    Reply
  8. Monika on

    Very detailed and clear explanation. Thanks a lot!! 🙂 Would be helpful if you could explain the compile function of custom directive. Thanks in advance 🙂

    Reply
  9. michel A. on

    Very nice explanation. As a newby in Angular I could understand a lot of things.
    Thanks a lot !

    Reply
  10. Karthi on

    Akka Superu…

    Reply
  11. Sadashiv J P on

    Seriously your content on Custom directives is damn good!!. I know other contents of Angular JS 1.x and working on the same from around last 1 year. I used custom directives in my page which is already being developed by my team members. But, I was struggling to understand custom directive and its creation. Your content helped me a lot in understanding those stuffs. Thanks a lot!!. Hope you will publish some more useful in Angular JS in future.

    Reply
  12. Sachin on

    Very nice explanation with example, cleared all my doubts. Thank you.

    Reply
  13. devesh on

    Very nice !!
    Please keep posted these tutorials

    Reply
  14. suresh on

    Good Explanation!.. thanks

    Reply
  15. Malvi Panchal on

    Well Explained ! This helped me..Thanks..!

    Reply
  16. jaikishan roy on

    very useful tutorial..
    thanks

    Reply
  17. rolex on

    very cool. Worked like a charm..

    Reply
  18. palz pal on

    Excellent article..

    Reply
  19. Mahesh on

    Really useful for me. I was confused on direcrive scope before reading this article. But now I’m pretty satisfied and properly understood.

    Reply
  20. Ramesh on

    well explained!

    Reply
  21. mahesh on

    Helpful article

    Reply
  22. Anand on

    Very nice explanation

    Reply
  23. Anukrishna V on

    Thank You

    Reply
  24. Sathish kumar on

    Thank you for this tutorial
    Really superb!!
    Clear explanation, very useful

    Reply
  25. hp on

    very damn good explanation

    really awesome to understand

    Reply
  26. satya panda on

    You have explained everything in very detail manner. I was trying to run your code in visual studio, everything ran , but the display() function did’nt work, without using the link property.

    I liked your article very much, you have done a lot of hard work. keep it up and thanks.

    Satya Panda

    Reply
  27. Vijayalakshmi N on

    Great explanation… I understood very clearly.Thank you so much!

    Reply
  28. Abhishek Ranjan on

    Great explained….

    Reply
  29. Goutamendu Hait on

    I just have started learning Angular. This article was really easy to understand the directive concepts. Thank you . It helped a lot .

    Reply
  30. Mahesh B on

    Awesome !!!!

    Reply
  31. Leandro on

    Nice… very useful… tks

    Reply
Leave a Reply

Related Posts

Designing the Future & the Future of Work – The I... Martin Wezowski, Chief Designer and Futurist at SAP, shares his thoughts on designing the future and the future of work on this episode of The Innovat...
The 4 Characteristics of a Healthy Digital Product Team Several weeks ago, I found myself engaged in two separate, yet eerily similar, conversations with CEOs struggling to gain the confidence they needed t...
Recapping Fortune Brainstorm Tech – The Innovation Eng... On this episode of The Innovation Engine, David DeWolf and Jonathan Rivers join us to share an overview of all the news that was fit to print at this ...
4 Reasons Everyone is Wrong About Blockchain: Your Guide to ... You know a technology has officially jumped the shark when iced tea companies decide they want in on the action. In case you missed that one, Long Isl...
The Connection Between Innovation & Story On this episode of The Innovation Engine, we'll be looking at the connection between story and innovation. Among the topics we'll cover are why story ...