【问题标题】:Why AngularJS directive compiling not support nested directives为什么AngularJS指令编译不支持嵌套指令
【发布时间】:2013-08-26 02:42:06
【问题描述】:

在我的理解中,$compile 应该能够支持嵌套指令编译/链接,但是我们遇到了一个编译/链接不完整的问题——只有最外层的指令被渲染到 DOM 中,并且只有在两者都出现时才会重现该问题以下条件为真:

  1. 内部指令模板通过templateUrl加载(例如异步方式)
  2. 编译是在 Angular 上下文之外触发的。

我写了一个jsfiddler来演示,部分代码如下,完整案例http://jsfiddle.net/pattern/7KjWP/

myApp.directive('plTest', function($compile){
return {
    restrict :'A',
    scope: {},
    replace: true,
    template: '<div>plTest rendered </div>',
    link: function (scope, element){
        $('#button1').on('click', function(){
            var ele;
            ele = $compile('<div pl-shared />')(scope); 
            console.log('plTest compile got : '+ ele[0].outerHTML);
           // scope.$apply();
            element.append(ele);
        });
     }
};
});

myApp.directive('plShared', function($compile, $timeout){
return {
    restrict: 'A',
    scope: {},
    replace: true,
    link: function (scope, element){
        // comment out below line to make render success
        //$timeout(function(){});
        var el = $compile('<div pl-item></div>')(scope);
        console.log('plShared compile got:' + el[0].outerHTML);
        element.append(el);
    }
};
});

myApp.directive('plItem', function($timeout){
return {
    restrict: 'A',
    scope:{},
    template:'<div>plItem rendered <div pl-avatar/></div>',
    link: function(scope){            

    }
};
});

myApp.directive('plAvatar', function(){
return {
    restrict: 'A',
    scope: {}
   , templateUrl: 'avatar.html'
  //  ,template: 'content of avatar.html <div pl-image></div>'
};
});

有趣的是,我可以通过在 compile() 调用之后的某个地方调用 scope.$apply() 来解决这个问题(第 27 行) 或将 $timeout(function(){}) 调用添加到内部指令之一的链接函数中(第 41 行)。这是缺陷还是设计使然?

【问题讨论】:

  • 这就是 2-way 绑定的工作原理,要么你在 Angular 上下文中进行更改,要么你必须自己调用 $apply()。
  • 正如@stevuu 解释的那样,您代码中的实际问题是这一行:` $('#button1').on('click',..` - angular 不知道这除非您致电 $scope.$apply(),否则事件已由您的代码处理
  • 我知道有必要在角度上下文之外调用 scope.$apply() 以推出 $evalAsyc 和 $watch 检查,但令我困惑的是为什么指令编译与那些支票。猜测可能是模板以异步方式加载,这样 Angular 可能没有机会在所有异步加载完成之前链接所有模板,因此需要 $apply() 显式解决来自 $evalAsyc 队列的那些加载承诺。如果是这样,那么额外的$apply()是合理的,否则不一致有点烦人。
  • 顺便说一句,只是从角度源代码中找到, $timeout(function{}) 也会在内部触发 $rootScope.$apply() 调用。默认情况下会触发呼叫。如果我使用 $timeout(function{}, 0, false) 跳过调用。第二个 $timeout 解决方案不起作用。
  • 我遇到了同样的问题,通过添加 $apply 也解决了,感谢您提供的信息。但是我仍然感到困惑,因为在我的情况下,HTML 始终是异步加载的,并且问题仅出现在一种情况下——HTML 先前已加载并且在 $compile 和重新插入之前被 .removed。我会认为 $compile 总是递归的,为什么在 $compile 之后需要 $apply?

标签: angularjs


【解决方案1】:

$(foo).on(bar, handler) 是一个 jQuery 事件,这意味着 AngularJS 不知道它的细节,并且不会(不能)在它之后运行apply-digest cycle 来处理所有绑定。

scope.$apply 就是为此而生的,正如你所说的,修复它。经验法则是:如果您使用其他库在 AngularJS 应用程序中实现 UI 功能(特别是:在应用摘要周期之外),您必须自己调用 scope.$apply

HTH!

【讨论】:

    【解决方案2】:

    在 element.append(el) 之后,再次尝试编译,因为你刚刚修改了 DOM。

    你可以试试$compile(element)(scope);$compile(element.contents())(scope);

    正如我之前所说,我还将按如下方式更改事件处理程序:

    $('#button1').on('click', function(){
         scope.$apply( function(){
              //blablalba
         });
    });
    

    另外,如果您想缩小代码,我将使用以下语法声明编译依赖项:

    .directive('directiveName',['$service1',''$service2,...,'$compile', function($service1, $service2,...,$compile){
         //blablabla
    }]}
    

    【讨论】:

      猜你喜欢
      • 2016-01-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多