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

13

我有一个指令附加在动态生成的<table>元素内的模板中。该指令在link函数内操作该表的DOM。问题是,该指令在表格呈现之前运行(通过评估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 */
        }
    };
});

1
您可以在表格标签中添加ng-if,条件可以是当表格数据加载完成时。 - Hmahwish
你的数据是来自 $http 请求还是硬编码数据? - Kalhan.Toress
@K.Toress 数据来自HTTP请求,但在处理该模板时已经加载 - 路由和控制器通过$routeProvider配置中的resolve属性来处理。 - Robert Kusznier
2
可以在ng-repeat的行上放置一个指令,并检查$last是否作为您操作的触发器。 - charlietfl
1
@DeblatonJean-Philippe 我仍然不完全理解为什么指令运行的顺序是如此,但肯定不是因为指令的优先级。优先级只在应用于同一元素的指令之间起作用,如下文所述 Directive Definition Objectpriority:https://docs.angularjs.org/api/ng/service/$compile#directive-definition-object - Robert Kusznier
显示剩余9条评论
3个回答

6
尝试使用$timeout将您链接函数中的代码包装起来,这样它将在DOM呈现后执行。
$timeout(function () {
    //do your stuff here as the DOM has finished rendering already
});

不要忘记在您的指令中注入$timeout:
.directive("directiveName", function($timeout) {

虽然有很多替代方案,但我认为这个更加简洁,因为$timeout会在渲染引擎完成工作后执行。


6

一般情况下,仅凭<table>元素上的指令是无法完全确定的。

但在某些情况下,您可以确定。在您的情况下,如果内部内容被ng-repeat重复,则如果ngRepeat工作的项目数组已准备好,则实际的DOM元素将在循环结束时准备就绪。您可以在0延迟的$timeout后捕获它:

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

但是一般情况下,你无法确定要捕获的内容。如果ngRepeat的数组还没有出现怎么办?或者有一个ng-include呢?

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

或者,如果有一个定制的指令,它的工作方式与ngRepeat不同,会怎样呢?
但是,如果您完全控制内容,一种可能的方法是将某些helper指令作为最内层/最后一个元素包含,并在连接时让它联系其父指令名称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();
    }
  }
})

2
这是我认为最全面的答案。谢谢。我仍然不明白为什么使用0延迟的$timeout可以保证DOM已准备就绪,但我想我会在Angular的$timeout文档中找到答案。 - Robert Kusznier
2
@Robert,“ng-repeat”有一个$scope.$watchCollection - 在链接阶段之后触发,如果数组已准备好,则传输ng-repeat的模板并将其放置在DOM中。$timeout带0延迟立即执行。 - New Dev

0
一个干净的方法是使用类似 lodash 的 _.defer 方法 这样的东西。
在你的链接中使用 _.defer(your_func, your_func_arg1, your_func_arg2, ...) 调用它,以便在当前调用栈已清除并且一切准备就绪时执行该方法。
这样,您就不必自己估计 $timeout

1
_.defer 简单地调用 setTimeout,没有延迟,尽管它对您传递的参数进行了一些运行时检查。除非您已经在使用 lodash,否则最好使用 NG 内置的 $timeout,没有延迟。 - jusopi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接