问题 AngularJS指令在元素完全加载之前运行


我有一个附加到动态生成的指令 <table> 模板内的元素。该指令操纵该表内的DOM link 功能。问题是指令在表呈现之前运行(通过评估 ng-repeat 指令) - 表是空的。

如何在表完全呈现后确保指令运行?

<table directive-name>
    <tr ng-repeat="...">
        <td ng-repeat="..."></td>
    </tr>
</table>


module.directive("directiveName", function() {
    return {
        scope: "A",
        link: function(scope, element, attributes) {
            /* I need to be sure that the table is already fully
               rendered when this code runs */
        }
    };
});

2112
2017-08-18 13:38


起源

您可以在表标记中添加ng-if,条件可以在加载表数据时添加 - Hmahwish
是你的数据来自 $http 请求或只是硬编码数据 - K.Toress
@K.Toress数据来自HTTP请求,但是在处理该模板时已经加载了 - 路由和控制器负责处理 resolve 属性 $routeProvider 组态。 - Robert Kusznier
可以在ng-repeat行上加上指令并检查 $last 作为你操纵的触发器 - charlietfl
@ DeblatonJean-Philippe我仍然不完全理解为什么指令按照它们的顺序运行,但它肯定不是因为指令的优先级。优先级仅适用于应用于同一元素的指令,如下所述 指令定义对象的 优先 : docs.angularjs.org/api/ng/service/... - Robert Kusznier


答案:


从一般意义上讲,你不能仅仅通过对指令的指示来“完全确定” <table> 元件。

但在某些情况下你可以肯定。在您的情况下,如果内部内容是 ng-repeat-ed,然后如果项目数组超过哪个 ngRepeat 工作准备就绪,然后在摘要周期结束时准备好实际的DOM元素。你可以在之后捕获它 $timeout 0延迟:

link: function(scope, element){
  $timeout(function(){
    console.log(element.find("tr").length); // will be > 0
  })
}

但是,从一般意义上讲,您无法确定捕获内容。怎么样? ngRepeated数组还没有?或者如果有的话怎么办? ng-include 代替?

<table directive-name ng-include="'templates/tr.html'">
</table>

或者,如果有一个自定义指令的工作方式不同于 ngRepeat 呢?

但是如果你完全控制了内容,一种可能的方法就是将一些辅助指令作为最里面/最后一个元素,并让它与它的父元素联系 directiveName 什么时候链接:

<table directive-name>
    <tr ng-repeat="...">
        <td ng-repeat="...">
          <directive-name-helper ng-if="$last">
        </td>
    </tr>
</table>
.directive("directiveNameHelper", function(){
  return {
    require: "?^directiveName",
    link: function(scope, element, attrs, ctrl){
      if (!ctrl) return;

      ctrl.notifyDone();
    }
  }
})

5
2017-08-18 13:56



我认为这是最全面的答案。谢谢。我仍然不明白为什么 $timeout 0延迟保证DOM准备就绪,但我想我会在Angular中找到它 $timeout文档。 - Robert Kusznier
@Robert, ng-repeat 有一个 $scope.$watchCollection  - 这在链接阶段之后触发,如果阵列准备就绪,那么它将转换为 ng-repeat-ed模板并将其放在DOM中。 $timeout 在此之后立即执行0延迟 - New Dev


尝试包装 $timeout 链接函数中的代码,因为它将在呈现DOM后执行。

$timeout(function () {
    //do your stuff here as the DOM has finished rendering already
});

别忘了注射 $timeout 在你的指令中:

.directive("directiveName", function($timeout) {

有很多选择,但我认为这个更清晰,因为$ timeout在渲染引擎完成其工作后执行。


4
2017-08-18 13:54





一个干净的方法是使用类似的东西 lodash的 _.defer 方法

你可以用它来打电话 _.defer(your_func, your_func_arg1, your_func_arg2, ...) 在你的链接里面执行方法,当前的调用堆栈已经清除并且一切准备就绪。

这样,您就不必估计了 $timeout 靠自己。


0
2017-08-18 14:00



_.defer 只是打电话 setTimout 没有延迟,虽然它对你传入的参数进行了一些运行时检查。除非你已经使用了lodash,否则你也可以使用NG内置的 $timeout 没有延迟。 - jusopi
不知道,谢谢! - Martin Seeler