0

I have a directive like the following:

.directive('myDirective', function() {
    return {
        restrict: 'AE',
        replace: true,
        templateUrl: '/myDirective.html?v=' + window.buildNumber,
        link: function (scope, element, attrs) {
            scope.itemClasses = attrs.itemClasses || '';
        }
    }
})

and it's template looks like:

<div class="my-directive">
    <div class="items" ng-repeat="item in items">
        <div class="item {{ itemClasses }}">{{ item.title }}</div>
    </div>
</div>

and that directive is called in different places (one call per template) like this:

<my-directive item-classes="col-md-6"></my-directive>
...
<my-directive item-classes="col-md-12"></my-directive>

And all templates render the same value of itemClasses. At the same time link function sets proper value (I checked this fact by call of console.log()).

And if I add scope: true attribute to the directive's code - then all works fine. So, it seems like having own inherited scope helps. Could you explain me such behaviour?

Thank you.

2 Answers 2

1

When you use scope: true you create a child scope for your directive and at the same time you inherit also the properties from the parent (in this case I think your controller). So all your directive's instances will have their proper scope.

Using scope: false your directive actually has no scope, and it shares the scope with its parent.

So, coming back to your question, having scope:false, when you do this:

scope.itemClasses = attrs.itemClasses || '';

you are setting the itemClasses property to the scope of the parent. It means that when you see in the link function it seems to work fine, but in reality every time you are going to override the same scope variable of the controller.

So, in your case, the first time you associate a scope variable to the parent controller:

scope.itemClasses= 'col-md-6';

but the second time actually you override the same scope variable of the parent with the new value:

scope.itemClasses= 'col-md-12';

Does it make sense?

Sign up to request clarification or add additional context in comments.

12 Comments

Thank you for explanation! Please, let me summarise to be sure that I got it right. All link functions are called before any template render. So, my code just override the same attribute in the scope. And when it comes to render the template I have just a last assigned value. Right?
So, is the scope: true approach is the right one? Or there's something better?
Briefly: with scope false you use the parent's scope. So you are always setting the same variable in the parent's scope. So you override always the same variable. For the right approach it depends on what you are going to do with this value. What you are going to do with itemClasses? You need to change them when some variable in the controller changes?
I just want to be able to pass some argument to a directive. That value will be rendered in the template. I mean, I have 3 HTML block with the same layout. The only difference between them is the class added to the element. So I decided to create a directive and call it with the arugment (that changing class).
And yes, I understand the idea that scope: false makes me use parent's scope. So, yes, it will always set the same variable. I thought that it works like: setVariable -> render; setVariable -> render (so there's no problem). And from your answer I got that it works like: setVariable -> setVariable; render->render. So, we render after all set procedures are done. Right? :)
|
1

By binding the item-classes directly from the attributes you are basically making a copy of the attribute's values.

The solution for this is: if you want to make input-classes an input binding you may use isolated scope like so:

{
    restrict: 'AE',
    replace: true,
    scope: {
        itemClasses: '='
    },
    templateUrl: '/myDirective.html?v=' + window.buildNumber,
    link: function (scope, element, attrs) {
        // scope.itemClasses = attrs.itemClasses || '';
    }
}

Or just in case you don't want to use isolated scope, you can user attrs.$observe:

link: function (scope, element, attrs) {
    attrs.$observe('itemClasses ', function(val) {
        scope.itemClasses = val || '';
    });
}

1 Comment

Thank you for your answer. AFAIU, isolated scope doesn't allow me to access attributes of outer scope. So, it's not the option for me. Taking into consideration the answer of @quirimmo, I'm not sure about 2nd solution. I mean, seems like it also will be executed before any template renders. I.e., on template render step we'll have last assigned value.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.