repeat not work inside a transclusion?

Okay, this may seem somewhat esoteric, but I am trying to solve a particular issue with a directive I have written: https://github.com/michaelbromley/angularUtils/issues/37

Here is a simplified version of what is going on.

This works:

I have a directive that delegates to ng-repeat by dynamically adding an ng-repeat attribute to the element during the compile stage, and then compiles that element:

myApp.directive('repeatDelegator', function($compile, $timeout) {
  return {
    compile: function(element, attrs){
        attrs.$set('ngRepeat', attrs.repeatDelegator); //Add ng-repeat to the dom
        element.removeAttr('repeat-delegator'); // remove the repeat-delegator to prevent infinite recursion of compilation

        var compiled =  $compile(element, null);
        return function(scope, element, attrs){
            compiled(scope);
        };
      }
   }
});

This is how the directive is called:

<ul>
    <li repeat-delegator="item in items">{{item}}</li>
</ul>

This works fine - see the first example here: http://plnkr.co/edit/oWd3rGAulsxoeSqffxMU?p=preview

However, when the repeat-delegator is put inside any other directive that uses transclusion, it does not work.

This does not work. Why?

Here is a basic directive that does nothing but causes transclusion:

myApp.directive('transcluder', function() {
    return {
        restrict: 'AE',
        transclude: true,
        scope: {},
        template: '<div ng-transclude></div>'
      };
});

So when we call the repeat-delegator directive inside this transclusion, it fails and nothing shows up:

<transcluder>
    <ul>
        <li repeat-delegator="meal in meals">{{meal}}</li>
    </ul>
</transcluder>

This is illustrated in the second example: http://plnkr.co/edit/oWd3rGAulsxoeSqffxMU?p=preview

What I know so far:

I have spent some hours stepping through the Angular.js source as this executes to try to figure out why it fails inside the transclusion, but I can't get to the bottom of it.

In the broken (transcluded) version, when I see the ngRepeat being compiled, the $scope seems to be correct (it is a child $scope of the main controller $scope, since the transclusion causes a new child $scope to be created). You can write "scope.items" in the console and see the list of items.

I am guessing something like this is happening:

  • The transcluder directive is compiled first because it is higher in the DOM tree, so it is encountered first.
  • The transclusion causes the directive's child nodes to be removed from the DOM and cloned into the $template var, for later insertion back into the DOM.
  • Perhaps this causes the ng-repeat to compile against a clone of the <li>..</li> node, which never actually gets attached back to the DOM?
  • I'm not sure though. It's a really tricky problem, and any help would be very much appreciated!


    Okay, after a day spent hacking away at the Angular.js source in dev tools, I figured out what was wrong here, and my guess above was basically right.

    The trouble was that the repeat-delegator directive would get compiled against the detached clone, so effectively the ng-repeated elements would be appended to a lost piece of DOM that would never get appended back to the body of the page.

    The solution was fairly simple: move the compilation of the repeat-delegator's delegated ng-repeat into the linking function (rather that in the compile stage, where it was originally).

    Doing this means that when the ng-repeat gets compiled, it is done against the correct DOM node which is now safely attached to the DOM below the transcluded directive.

    myApp.directive('repeatDelegator', function($compile, $timeout) {
        return {
          compile: function(element, attrs){
              attrs.$set('ngRepeat', attrs.repeatDelegator); //Add ng-repeat to the dom
              element.removeAttr(attrs.$attr.repeatDelegator); // remove the repeat-delegator to prevent infinite recursion of compilation
    
              return function(scope, element, attrs){
                  var compiled =  $compile(element);
                  compiled(scope);
              };
          }
       }
    });
    
    链接地址: http://www.djcxy.com/p/95238.html

    上一篇: 使用现有指令的AngularJS DOM操作

    下一篇: 重复在一个跨越里面不工作?