是否有Angular JS指令的后期渲染回调?

Nik*_* So 137 angularjs

我刚刚得到了我的指令来引入一个模板来附加到它的元素:

# CoffeeScript
.directive 'dashboardTable', ->
  controller: lineItemIndexCtrl
  templateUrl: "<%= asset_path('angular/templates/line_items/dashboard_rows.html') %>"
  (scope, element, attrs) ->
    element.parent('table#line_items').dataTable()
    console.log 'Just to make sure this is run'

# HTML
<table id="line_items">
    <tbody dashboard-table>
    </tbody>
</table>
Run Code Online (Sandbox Code Playgroud)

我还使用了一个名为DataTables的jQuery插件.它的一般用法是这样的:$('table#some_id').dataTable().您可以将JSON数据传递到dataTable()调用以提供表数据,或者您可以将数据放在页面上,然后它将完成其余的工作.我正在执行后者,将行放在HTML页面上.

但问题是我必须在DOM准备就绪之后调用表#line_items上的dataTable().上面的我的指令在模板附加到指令元素之前调用dataTable()方法.有没有办法可以在附加后调用函数?

谢谢您的帮助!

安迪回答后更新1:

我想确保链接方法仅在页面上的所有内容之后调用,因此我更改了指令以进行一些测试:

# CoffeeScript
#angular.module(...)
.directive 'dashboardTable', ->
    {
      link: (scope,element,attrs) -> 
        console.log 'Just to make sure this gets run'
        element.find('#sayboo').html('boo')

      controller: lineItemIndexCtrl
      template: "<div id='sayboo'></div>"

    }
Run Code Online (Sandbox Code Playgroud)

我确实在div#sayboo中看到了"boo".

然后我尝试我的jquery数据表调用

.directive 'dashboardTable',  ->
    {
      link: (scope,element,attrs) -> 
        console.log 'Just to make sure this gets run'
        element.parent('table').dataTable() # NEW LINE

      controller: lineItemIndexCtrl
      templateUrl: "<%= asset_path('angular/templates/line_items/dashboard_rows.html') %>"
    }
Run Code Online (Sandbox Code Playgroud)

那里没有运气

然后我尝试添加一个时间:

.directive 'dashboardTable', ($timeout) ->
    {
      link: (scope,element,attrs) -> 
        console.log 'Just to make sure this gets run'
        $timeout -> # NEW LINE
          element.parent('table').dataTable()
        ,5000
      controller: lineItemIndexCtrl
      templateUrl: "<%= asset_path('angular/templates/line_items/dashboard_rows.html') %>"
    }
Run Code Online (Sandbox Code Playgroud)

这很有效.所以我想知道代码的非计时器版本出了什么问题?

par*_*ent 211

如果未提供第二个参数"delay",则默认行为是在DOM完成渲染后执行该函数.因此,不要使用setTimeout,而是使用$ timeout:

$timeout(function () {
    //DOM has finished rendering
});
Run Code Online (Sandbox Code Playgroud)

  • 你是对的,我的答案有点误导,因为我试图让它变得简单.完整的答案是,这种效果不是Angular的结果,而是浏览器的结果.`$ timeout(fn)`最终调用`setTimeout(fn,0)`,它有效中断Javascript执行并允许浏览器首先呈现内容,然后继续执行该Javascript. (23认同)
  • 为什么不在[docs](https://docs.angularjs.org/api/ng/service/$timeout)中解释? (8认同)
  • 将浏览器视为队列某些任务,例如"javascript执行"和"DOM渲染",以及setTimeout(fn,0)在渲染后将当前运行的"javascript执行"推送到队列的后面. (7认同)
  • @GabLeRoux是的,这将产生相同的效果,除了$ timeout还有额外的好处,它运行后调用$ scope.$ apply().使用_.defer(),如果myFunction更改了作用域上的变量,则需要手动调用它. (2认同)
  • 我有一个场景,这对于第1页ng-repeat渲染一堆元素没有帮助,然后我转到第2页然后我回到第1页,我试图获得高ng-repeat元素父...它返回错误的高度.如果我超时1000毫秒,那么它的工作原理. (2认同)

Roy*_*ove 14

我有同样的问题,我相信答案真的不是.请参阅Miško的评论小组中的一些讨论.

Angular可以跟踪它操作DOM所做的所有函数调用是否完整,但由于这些函数可能触发在返回后仍然更新DOM的异步逻辑,因此无法预期Angular会知道它.Angular提供的任何回调有时可能会起作用,但依赖它是不安全的.

我们用setTimeout启发式地解决了这个问题.

(请记住,不是每个人都同意我的意见 - 你应该阅读上面链接上的评论,看看你的想法.)


And*_*lin 7

您可以使用"链接"功能,也称为postLink,它在放入模板后运行.

app.directive('myDirective', function() {
  return {
    link: function(scope, elm, attrs) { /*I run after template is put in */ },
    template: '<b>Hello</b>'
  }
});
Run Code Online (Sandbox Code Playgroud)

如果您计划制定指令,请阅读此内容,这是一个很大的帮助:http://docs.angularjs.org/guide/directive


con*_*uxe 7

虽然我的答案与数据表无关,但它解决了DOM操作的问题,例如jQuery插件初始化,用于在异步方式更新其内容的元素上使用的指令.

可以添加一个监听内容更改(甚至是其他外部触发器)的监视,而不是实现超时.

在我的情况下,我使用这种解决方法初始化一个jQuery插件,一旦完成ng-repeat创建我的内部DOM - 在另一种情况下,我用它来在控制器上更改scope属性后操作DOM.我是怎么做的......

HTML:

<div my-directive my-directive-watch="!!myContent">{{myContent}}</div>
Run Code Online (Sandbox Code Playgroud)

JS:

app.directive('myDirective', [ function(){
    return {
        restrict : 'A',
        scope : {
            myDirectiveWatch : '='
        },
        compile : function(){
            return {
                post : function(scope, element, attributes){

                    scope.$watch('myDirectiveWatch', function(newVal, oldVal){
                        if (newVal !== oldVal) {
                            // Do stuff ...
                        }
                    });

                }
            }
        }
    }
}]);
Run Code Online (Sandbox Code Playgroud)

注意:在my-directive-watch属性中,不仅可以将myContent变量强制转换为bool,而是可以想象那里有任意表达式.

注意:像上例中隔离范围只能在每个元素上执行一次 - 尝试在同一元素上使用多个指令执行此操作将导致$ compile:multidir错误 - 请参阅:https://docs.angularjs.org /错误/ $编译/ multidir


Mad*_*eep 6

回答这个问题可能要迟到了.但仍有人可能从我的答案中获益.

我有类似的问题,在我的情况下,我无法更改指令,因为它是一个库,更改库的代码不是一个好习惯.所以我所做的是使用一个变量来等待页面加载并使用ng-if在我的html中等待呈现特定元素.

在我的控制器中:

$scope.render=false;

//this will fire after load the the page

angular.element(document).ready(function() {
    $scope.render=true;
});
Run Code Online (Sandbox Code Playgroud)

在我的HTML(在我的情况下,html组件是一个画布)

<canvas ng-if="render"> </canvas>
Run Code Online (Sandbox Code Playgroud)