2
votes

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.

1
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 - FloG
Oh and I found what I think is an even dirtier solution (in my opinion): plnkr.co/edit/AiCiQtO44unq0d8hDBKa - FloG
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. - Sergiu Paraschiv
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. - Sergiu Paraschiv
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. - FloG

1 Answers

1
votes

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.