AngularJS:为什么委托ng-repeat在翻译中不起作用?

时间:2014-08-21 10:52:44

标签: javascript angularjs dom

好吧,这看起来有点深奥,但我试图用我写的指令来解决一个特定的问题:https://github.com/michaelbromley/angularUtils/issues/37

以下是正在进行的简化版本。

这有效:

我有一个指令,通过在编译阶段动态地向元素添加ng-repeat属性来委托ng-repeat,然后编译该元素:

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);
        };
      }
   }
});

这是指令的调用方式:

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

这很好用 - 请参阅此处的第一个示例:http://plnkr.co/edit/oWd3rGAulsxoeSqffxMU?p=preview

但是,当repeat-delegator放在任何其他使用翻译的指令中时,它都不起作用。

这不起作用。为什么?

这是一个基本指令,除了导致转换外什么都不做:

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

因此,当我们在此转换中调用repeat-delegator指令时,它会失败并且不显示任何内容:

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

第二个例子说明了这一点:http://plnkr.co/edit/oWd3rGAulsxoeSqffxMU?p=preview

到目前为止我所知道的:

我花了几个小时踩过Angular.js源代码,因为这会执行以试图找出它在转换中失败的原因,但我无法深入到它的底部。

在破碎(转换)版本中,当我看到正在编译的ngRepeat时,$ scope似乎是正确的(它是主控制器$ scope的子$ scope,因为转换会导致一个新的子$ scope到被创造)。您可以在控制台中编写“scope.items”并查看项目列表。

我猜这样的事情正在发生:

  • 首先编译transcluder指令,因为它在DOM树中更高,因此首先遇到它。
  • 转换导致指令的子节点从DOM中删除并克隆到$template var中,以便以后插入到DOM中。
  • 也许这会导致ng-repeat针对<li>..</li>节点的 clone 进行编译,而该节点实际上从未实际连接回DOM?

我不确定。这是一个非常棘手的问题,任何帮助都会非常感激!

1 个答案:

答案 0 :(得分:3)

好的,在开发工具中使用Angular.js源代码的一天后,我发现这里出了什么问题,我上面的猜测基本上是正确的。

麻烦的是,repeat-delegator指令将针对分离的克隆进行编译,因此有效地将ng重复的元素附加到丢失的DOM片段中,该片段永远不会附加到页面主体

解决方案非常简单:将repeat-delegator的委托ng-repeat的编译移动到链接函数中(而不是在编译阶段,最初的位置)。

这样做意味着当ng-repeat被编译时,它是针对正确的DOM节点完成的,该节点现在安全地连接到transcluded指令下面的DOM。

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);
          };
      }
   }
});