Renaud
Renaud

Reputation: 4668

angular directive scoping issue

I have some experience with angular now but I still cannot figure out that one. I have a directive2 transcluded through another directive1. Directive1 is on the same element as a controller and they share the same scope. I want directive2 to inherit from the scope in controller but instead of being parent/child they end up siblings. What am I missing here?

Here is the code:

angular.module('myModule', [])
    .controller('Controller', ['$scope', function($scope) {
        console.log('from controller', $scope);
    }])
    .directive('directive1', function(){
        return {
            restrict: 'A',
            replace: true,
            transclude: true,
            template:  '<div style="width:150px;"> <p>directive1</p>'
            +'<div style="width:100px;" ng-transclude></div> </div>',
            link: function($scope, elem, attrs){
                console.log('from directive1', $scope);
            }
        }
    })
    .directive('directive2',  function(){
        return {
            restrict: 'A',
            link: function($scope, elem, attrs) {
                console.log('from directive2', $scope);
            }
        }
    });

The html associated:

<div x-directive1 ng-controller="Controller">
    <div x-directive2> <p>directive2</p> </div>
</div>

And a link to the jsFiddle: http://jsfiddle.net/RFontana/Lm7gA/1/

Upvotes: 0

Views: 106

Answers (2)

Adam
Adam

Reputation: 5236

The issue is with placing the ng-controller directly on the parent directive that is doing the transclusion. Transclusion directives create a new scope. Create a wrapping element and place the ng-controller directive on that.

Like this:

<section ng-controller="Controller">
    <div x-directive1>
        <div x-directive2> <p>directive2</p> </div>
    </div>
</section>

I have forked your fiddle and placed the fix in it.

Why the inner directive was showing up as a sibling (from the official AngularJS Directive document):

transclude - compile the content of the element and make it available to the directive. Typically used with ngTransclude. The advantage of transclusion is that the linking function receives a transclusion function which is pre-bound to the correct scope. In a typical setup the widget creates an isolate scope, but the transclusion is not a child, but a sibling of the isolate scope. This makes it possible for the widget to have private state, and the transclusion to be bound to the parent (pre-isolate) scope.

In your case you were not declaring an isolate scope on the parent directive, but you were declaring the controller on the parent directive. The transclusion caused the the child directive to become a sibling scope to its parent and the controller, since the controller was on the parent.

My recommendation, avoid placing ng-controller on directives.

I hope this helps.

Upvotes: 2

aet
aet

Reputation: 7292

.directive('directive2', function() {
    return {
        restrict: 'A',
        controller: 'Controller',
        link: function(scope, elem, attrs) {
            console.log('from directive2', scope);
        }
    }
});

I would not use $scope as the named argument to those link functions, but 'scope' instead. This gives directive2 that Controller, but I don't think this is really what you want. Could you not just move the controller to an enclosing div? Then directive2 would in fact inherit the controllers scope.

<div ng-controller="Controller">
    <div x-directive1>
        <div x-directive2> <p>directive2</p> </div>
    </div>
</div>

Upvotes: 1

Related Questions