My goal is to create a flexible set of directives for reusable and lightweight UI elements. Each has an isolated scope, and many of them transclude content. I want each directive to be a black box - the user ideally wouldn't need to know whether it nests another directive internally when writing the content to be transcluded.
According to the Angular guide to directives:
The transclude option changes the way scopes are nested. It makes it so that the contents of a transcluded directive have whatever scope is outside the directive, rather than whatever scope is on the inside. In doing so, it gives the contents access to the outside scope.
I have found that this works as described when using a single directive. However, if there is another directive nested in that one that also transcludes the content, then the transcluded content is resolved within the scope of the outer directive, rather than the scope that is on the outside. This is a problem, because it prevents users from knowing in what scope their transcluded content will be resolved!
For example: (fiddle)
.controller('main', function ($scope) {
$scope.value = '"main"';
$scope.expected = $scope.value;
})
.directive('outer', function () {
return {
restrict: 'E',
replace: true,
transclude: true,
scope: { expected:'=' },
controller: function ($scope) {
$scope.value = '"outer"';
},
template: '<div><inner expected="expected"><span ng-transclude></span></inner></div>'
};
})
.directive('inner', function () {
return {
restrict: 'E',
replace: true,
transclude: true,
scope: { expected:'=' },
controller: function ($scope) {
$scope.value = '"inner"';
},
template: '<div><span>\'value\' is expected to be resolved in scope {{expected}}, and is resolved in scope </span><span ng-transclude></span></div>'
};
})
And the HTML:
<div ng-controller="main">
<inner expected="value">
<span>{{value}}</span>
</inner>
<hr/>
<outer expected="value">
<span>{{value}}</span>
</outer>
</div>
Inside the <inner></inner>
element {{value}}
is evaluated within the parent scope as "main" (as expected). However, inside the <outer></outer>
element {{value}}
is evaluated within the isolated scope of outer
as "outer" (not expected). In this way, the template of the directive can affect the scope in which the transcluded content is resolved!
Is there any way to work around this issue?