FloG
FloG

Reputation: 471

AngularJS : directive with ng-repeat and isolated scope

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

- number: {{item}}

to be repeated for each collection item and the {{item}} expression to be linked with the isolated scope.

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

Answers (1)

Sergiu Paraschiv
Sergiu Paraschiv

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

Related Questions