Reputation: 471
I've been trying to create a directive that takes the content of the element and wraps it with an ng-repeat. The trick is that the expressions in the content of the element have to be linked with the isolated scope of the directive (so I can't use ng-transclude).
I found a solution that seemed perfect: https://github.com/angular/angular.js/issues/7874#issuecomment-46410994 But it doesn't work with an isolated scope which isn't satisfactory, I require an isolated scope in order not to pollute the parent scope.
In the following plnkr, you can try commenting line 10, and it will work. http://plnkr.co/edit/B72H9Sd5Cs1Qc8GoTRGF
<test>
<h3>- number: {{item}}</h3>
</test>
app.directive('test', function(){
return {
restrict: 'E',
scope: {}, // try commenting this line
compile: function compile(tElement, tAttrs, tTransclude) {
var children = tElement.children();
var template = angular.element('<div ng-repeat="item in collection"></div>');
template.append(children);
tElement.html('');
tElement.append(template);
return function(scope, iElement, iAttrs, controller) {
scope.collection = [1, 2, 3, 4, 5];
};
}
};
});
What I would like is the
I don't understand the behavior. It seems to me like the {{item}} expression should be linked to the directive's scope (through the ng-repeat). Instead, it's linked with the parent scope.
Could somebody please help me understand and fix this?
Thank you.
Upvotes: 2
Views: 858
Reputation: 10153
Well, try this fiddle I added some stuff to:
app.directive('test', function(){
return {
restrict: 'E',
scope: {}, // try commenting this line
compile: function compile(tElement, tAttrs, tTransclude) {
var children = tElement.children();
var template = angular.element('<test2>{{collection}}<div ng-repeat="item in collection" ></div></test2>');
template.append(children);
tElement.html('');
tElement.append(template);
return function(scope, iElement, iAttrs, controller) {
scope.collection = [1, 2, 3, 4, 5];
console.log('test', scope);
};
}
};
});
app.directive('test2', function(){
return {
restrict: 'E',
scope: false,
link: function($scope) {
console.log('test2', $scope);
}
};
});
Basically when you add stuff to the DOM in compile: function compile(tElement, tAttrs, tTransclude) {
that code executes before the linking phase (the code running in return function(scope, iElement, iAttrs, controller) {
.
If you look at the two console.log
calls you'll see that test2
occurs before test
.
Couple this with an isolated scope on test
and you end up with the scope in test
being a child of the scope in test2
. Counter-intuitive, I know, but it's the way AngularJS's compiling/linking phases work.
Thus collection
is undefined
in test2
so ng-repeat
has nothing to "repeat" on.
If you remove scope: {}
you are basically telling test
's scope to be the same as test2
's (they are references to the same object), so collection
will (eventually) be defined in all directives, including ng-repeat
.
Upvotes: 1