2

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.

9
  • In the end, I was actually successful but I used a method that I don't find very clean (I manually compiled the directive) plnkr.co/edit/pYEDQa8R31ZDVQFDbDcS Commented Jan 19, 2015 at 13:24
  • Oh and I found what I think is an even dirtier solution (in my opinion): plnkr.co/edit/AiCiQtO44unq0d8hDBKa Commented Jan 19, 2015 at 13:28
  • 1
    Why was that not clear? The link phase runs after the compile phase of a directive. AngularJS directives are compiled in the compile phase. You are defining a link function for your directive, thus it is running after the compile phase. Adding AngularJS directives to element in the link phase won't compile them. So you manually need to compile them using $compile. Commented Jan 19, 2015 at 13:28
  • The "dirty" solution is actually less dirty. That template function you defined is called before the compile phase, thus the data-ng-repeat directive is compiled later in the compile phase, unlike when adding it in the linking phase. Commented Jan 19, 2015 at 13:30
  • Sergiu, I understand why I need to compile the directive in the solution I found, but I'm trying to make the "clean" solution work with an isolated scope. Commented Jan 19, 2015 at 13:31

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

2 Comments

plnkr.co/edit/z2krlO9TiEVpgjXfpntX?p=preview Actually it looks like test and test2's isolated scopes are siblings. But I guess what this means is that it's not directly possible to do what I want in that manner. So I'll keep to my other 2 solutions. I should have thought of looking at scope ids to understand the issue. Thank you for the help.
Well, yes, they have different IDs because they are created with $scope.$new but they are not necessarily isolated: docs.angularjs.org/api/ng/type/$rootScope.Scope#$new - prototypical inheritance is used.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.