Reputation: 37724
I am using a transcluding directive that, itself, is using a controller, that should redefine the inner scope, but isn't. And no matter how many tricks I use, I can't seem to get this done (leading me to believe I am doing something wrong).
The code is, reduced to a simple case, looking like this:
HTML:
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.1/angular.min.js"></script>
</head>
<body ng-controller="MainCtrl">
<mydirective>
{{foo}}
</mydirective>
</body>
</html>
JavaScript:
angular.module('app', [])
.controller('MainCtrl', function($scope) {
$scope.foo = 'bar';
})
.controller('InDirectiveController', function($scope) {
$scope.foo = 'baz';
})
.directive('mydirective', function() {
return {
restrict: 'EA',
scope: false,
template:'<div ng-controller="InDirectiveController">'+
'{{foo}} <inside></inside>'+
'</div>',
transclude:true,
link: function(scope, element, attrs, ctrl, transclude) {
transclude(scope, function(clone, scope) {
element.find("inside").append(clone);
});
}
};
});
Example here
http://jsbin.com/wuqoqalenu/1/edit?html,js,output
I would expect the output to be "baz baz", since I am specifically using transclude function. However, it is actually "baz bar", and I am not sure how to force the scope of the transcluded code to use the scope I am setting in the controller.
Upvotes: 1
Views: 74
Reputation: 11190
Without replace:true, you essentially get the following for the directive HTML (before compilation)
<mydirective>
<div ng-controller="InDirectiveController">
{{foo}} <inside></inside>
</div>
<mydirective>
When this is compiled, the directive scope remains the same as the outer (MainCtrl) scope, so the transcluded content is going to be evaluated against that scope. Thus the first {{foo}} is "baz" (from InDirectiveController), and the transcluded content {{foo}} is "bar" (from MainCtrl).
With replace:true, the compiled directive HTML becomes:
<div ng-controller="InDirectiveController">
{{foo}} <inside></inside>
</div>
When angular compiles this, ng-controller creates a new scope that inherits from MainCtrl's scope, which is associated with the directive's DOM node, so it becomes the directive's scope. Thus, when you do the transclude, it is now using InDirectiveController's scope, so you get "baz baz".
UPDATE to address question from comment:
Directives don't always create new scopes. When you use
scope: false
the directive scope will be the same as the "outside scope". So when the link function for the directive runs, it will be the "outside scope".
The transclude function uses the scope passed as the first argument as the scope for the transcluded content. In your first example, this scope is the scope passed into the link function, which in the first case will be the "outside scope".
When you set replace: true, this still doesn't cause a new scope to be created. However, your template uses ng-controller on it's outer element. Angular compiles by traversing "down" the DOM, and links by traversing back up. So it compiles the directive, then compiles the template, and then links the template (calling ngController's link function which creates a new scope), and then the directive's link function with the scope of the directive's template element, which is now the scope created by ng-controller (rather than the "outer scope").
UPDATE: Directive implementation without mg-controller
.directive('mydirective', function() {
return {
restrict: 'EA',
scope: true,
controller: function($scope) {
$scope.foo = 'baz';
},
template:'<div>'+
'{{foo}} <inside></inside>'+
'</div>',
transclude:true,
link: function(scope, element, attrs, ctrl, transclude) {
transclude(scope, function(clone, scope) {
element.find("inside").append(clone);
});
}
};
});
Upvotes: 3
Reputation: 37724
Adding replace: true
somehow magically fixed it.
I have no idea why, but it did.
Example here
http://jsbin.com/muhomuxuse/2/edit
edit:
According to angular documentation, "replace" parameter is being deprecated and should have no effect at all at scope inheritance. However, it has, and I am not really sure if it's a bug or a feature.
Upvotes: 0