ng-transclude should not create new sibling scope. #5489
Description
This is more of a change request and I would like to see what other people think.
In my humble opinion ng-transclude should not create it's own scope or at least have a way to prevent it from doing so. The reason behind this is that a directive which requests transclusion already has means to specify whether or not it wants to have a scope or an isolated scope or no scope at all. It uses ng-transclude directive to mark where it wants to insert the content. When ng-transclude creates it's own sibling scope it kind of breaks the expectations of the directive which defines what kind of scope it wants and there comes the manifestation of the popular 'value' vs 'object.value' confusion.
Here's an example of where new scope doesn't make sense in my opinion:
ui.directive('box', function() {
return {
restrict: 'E',
transclude: true,
template: '<div ng-transclude/>',
replace: true,
scope: {}
};
});
All this directive wants is replace the <box>content</box>
with a <div>content</div>
and the content to have the isolated scope.
Creating nested structure of directives like this leads to a scope tree pollution. Here's a plunker example (http://plnkr.co/edit/DwukVGGprFFjQuVY8yTz) of three nested directives which create a scope tree structure like this:
< Scope (002) : ng-app
< Scope (003) : ng-controller
< Scope (004) : box
< Scope (005) : ng-transclude
< Scope (006) : box
< Scope (007) : ng-transclude
< Scope (008) : box
< Scope (009) : ng-transclude
This behaviour doesn't seem to add any value to its purpose but creates a lot of confusion among beginners.
At the moment I use the following workaround which achieves exactly what the previous example does:
ui.directive('box', function() {
return {
restrict: 'E',
transclude: true,
template: '<div/>',
replace: true,
scope: {},
link: function(scope, element, attrs, transclude) {
transclude(scope.$parent, function(content) {
element.append(content);
});
}
};
});
Here's a plunker example (http://plnkr.co/edit/46v6IBLkhS71L1WbUDFl) which illustrates this concept. It leaves the scope tree nice and tidy:
< Scope (002) : ng-app
< Scope (003) : ng-controller
< Scope (004) : box
< Scope (005) : box
< Scope (006) : box
And the 2-way binding works the way many expect when they bind 'value' rather than 'object.value'. (I believe that the fact that passing just 'value' works in some cases but not the other and blaming the nature of prototypal inheritance in javascript is not a good excuse. The fact that many people find this behaviour unexpected indicates that there's an architectural flaw.)
I would love to hear what other people think and use cases where they think that creating a new sibling scope for ng-transclude makes sense.