Home » JavaScript » Angular.js » Learning Angular: What is the scope of your directive?

About Juri Strumpflohner

Juri Strumpflohner
Juri Strumpflohner mainly operates in the web sector developing rich applications with HTML5 and JavaScript. Beside having a Java background and developing Android applications he currently works as a software architect in the e-government sector. When he’s not coding or blogging about his newest discoveries, he is practicing Yoseikan Budo where he owns a 2nd DAN.

Learning Angular: What is the scope of your directive?

This might create quite some debugging headaches, especially in a larger application. Things don’t bind properly, have different data on the scope than you’d expect etc.. When you define your directives, you have to be really careful about their scope.

This article is part of my “Learning NG” series, presenting some of my adventures while learning Angular. Check out the series intro and other articles. Note, I’m an Angular newbie, so I’m more than happy for any kind of feedback and improvement suggestions from more experienced people than me.

 

The issue

Take the following two directives defined in your HTML as follows:

<first-directive></first-directive>
<second-directive></second-directive>

Here’s how they look internally.

.directive('firstDirective', function() {
    return {
      restrict: 'E',
      template: '<div>First directive: {{ vm.msg }}</div>',
      controller: function() {
        var vm = this;
        vm.msg = 'Hi';
      },
      controllerAs: 'vm'
    };
  })
  .directive('secondDirective', function() {
    return {
      restrict: 'E',
      template: '<div>Second directive: {{ vm.msg }}</div>',
      controller: function() {
        var vm = this;
        vm.msg = 'Hello World';
      },
      controllerAs: 'vm'
    };
  });

Nothing strange, right? Each of them defines a directive controller which sets a variable msg. What do you think is the expected output?

Hmm..not really, right? Clearly you would expect to get two different messages, “Hi” and “Hello World”. If you open the inspector (of your Chrome Devtools) you might understand why:

There's just one scope (ng-scope) defined

There’s just one scope (ng-scope) defined

As you can see, there’s only one scope defined. Take a look at the <html> tag at the very top: Angular nicely marks the scope with the ng-scope class. You can also visualize it with Batarang, the Angular.js Chrome Devtools extension.

Scopes being highlighted by Batarang

Scopes being highlighted by Batarang

To do so, simply activate the according “Show scope” option.

Activate the "Show scope" property on Batarang

Activate the “Show scope” property on Batarang

So the issue is quite clear, the directives don’t create a separate, isolated scope and thus, the vm variable of the directive controller on <second-directive> overwrites the vm variable of the directive controller on <first-directive>. Thus, two times “Hello World”.

Directive controllers don’t create a separate scope out of the box!

Interestingly, while a normal controller creates a scope, the directive controller doesn’t do so. It totally makes sense, but you might easily fall into this trap.

The solution

Directives can be forced to create an isolated scope. This is done by using the scope property, in the simplest case by setting it to true.

.directive('secondDirective', function() {
  return {
    ...
    scope: true,
    ...
  };
});

And voilá..

Also the HTML reflects the change, now having three different scopes.

Directives are now isolated, having their own scope

Directives are now isolated, having their own scope

Background

Some more background around scopes in directives.

The scope variable can either have true or false (default) as we’ve just seen, or you pass an object explicitly specifying which properties should be passed from outside into the directive (basically for configuring it).

angular.directive('myDirective', function(){
  return {
    ...
    scope: {
      obj1: '=',
      obj2: '@',
      obj3: '&'
    }
    ...
  };
});

= stands for data binding, it passes a reference to an object inside your directive. It keeps the expression in the attribute in sync with the isolated scope, thus allows for 2-way bindings.

<my-directive obj1='vm.someObject'></my-directive>

@ stands for interpolation. It is used with {{}} and will always return a string.

<my-directive ob1='{{ vm.msg }}'></my-directive>

& stands for expression. Within the directive it will be made available as a function, that, when called, executes the passed expression. Use it like

<my directive callback='vm.showAlert('Hi')'></my-directive>

..with your directive configuration object being defined for instance like this:

return {
    ...
    scope: {
        callback: '&'
    },
    template: '<button ng-click='callback()'>Click</button>'
    ...
}

You can also have different names for the scope properties that are used internally or passed externally:

scope: {
    obj1: '=object1',
    obj2: '&object2',
    obj3: '@object3'
}

You would use obj1 in your directive template, while the user of your directive would use object1 on the HTML.

Conclusion

While this example is obviously a simplified version where it’s quite easy to track to the problem, believe me, I invested some good time in understanding why suddenly my sidebar suddenly didn’t display any data. Only after I realized that the it’s directive scope contained the data of the <header> directive’s scope I started to get suspicious.

The learning from this is that you should pay attention to the scope of your directive. Whether you should isolate it or rather rely on the parents scope. An “attribute directive”, augmenting an existing element like a button or input box with additional features might not necessarily need to be isolated. Instead, you might expect an isolated scope when using more closed, complete widgets. Just think about it.

(0 rating, 0 votes)
You need to be a registered member to rate this.
Start the discussion Views Tweet it!
Do you want to know how to develop your skillset to become a Web Rockstar?
Subscribe to our newsletter to start Rocking right now!
To get you started we give you our best selling eBooks for FREE!
1. Building web apps with Node.js
2. HTML5 Programming Cookbook
3. CSS Programming Cookbook
4. AngularJS Programming Cookbook
5. jQuery Programming Cookbook
6. Bootstrap Programming Cookbook
and many more ....
I agree to the Terms and Privacy Policy

Leave a Reply

avatar

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  Subscribe  
Notify of