有角度 JS 指令的后渲染回调吗?

我刚刚得到我的指令,拉入一个模板,像这样附加到它的元素:

# 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>

我还在使用一个名为 DataTables 的 jQuery 插件。它的一般用法如下: $(‘ table # some _ id’)。DataTable ().您可以将 JSON 数据传递给 dataTable ()调用以提供表数据,或者您可以将数据放在页面上,它将完成其余的工作。.我正在做后者,在 HTML 页面上已经有了行。

但问题是,我必须调用表 # line _ item AFTER DOM 上的 dataTable ()。我上面的指令在模板被附加到指令的元素之前调用 dataTable ()方法。有没有一种方法可以在追加之后调用函数?

谢谢你的帮助!

安迪回答后更新1:

我想确保 link 方法只有在所有内容都在页面上之后才会被调用,所以我修改了一个小测试的指令:

# 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>"


}

我确实在 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') %>"
}

运气不好

然后我试着加上一个暂停:

.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') %>"
}

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

165383 次浏览

您可以使用“ link”函数,也称为 postLink,它在模板放入之后运行。

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

如果你打算做指令,读一下这个,它是一个很大的帮助: http://docs.angularjs.org/guide/directive

我也有同样的问题,我相信答案是否定的。参见 美子的评论和一些 小组讨论

Angular 可以跟踪所有它用来操作 DOM 的函数调用是否完成,但是由于这些函数可能触发异步逻辑,在它们返回后仍然在更新 DOM,因此不能期望 Angular 知道这一点。任何回调角度给 也许吧工作有时,但不会是安全的依赖。

我们使用 setTimeout 启发式地解决了这个问题,正如您所做的那样。

(请记住,并不是每个人都同意我的观点——你应该阅读以上链接的评论,看看你的想法。)

我也有同样的问题,但是使用 Angular + DataTable 和一个 fnDrawCallback + 行分组行分组 + $编译的嵌套指令。我在 fnDrawCallback函数中放置了 $timeout 来修复分页渲染。

例如,基于 row _ group 来源:

var myDrawCallback = function myDrawCallbackFn(oSettings){
var nTrs = $('table#result>tbody>tr');
for(var i=0; i<nTrs.length; i++){
//1. group rows per row_grouping example
//2. $compile html templates to hook datatable into Angular lifecycle
}
}

例如:

var myDrawCallback = function myDrawCallbackFn(oSettings){
var nTrs = $('table#result>tbody>tr');
$timeout(function requiredRenderTimeoutDelay(){
for(var i=0; i<nTrs.length; i++){
//1. group rows per row_grouping example
//2. $compile html templates to hook datatable into Angular lifecycle
}
,50); //end $timeout
}

即使是短暂的超时延迟也足以让 Angular 渲染我编译的 Angular 指令。

我得到这个工作与以下指令:

app.directive('datatableSetup', function () {
return { link: function (scope, elm, attrs) { elm.dataTable(); } }
});

在 HTML 中:

<table class="table table-hover dataTable dataTable-columnfilter " datatable-setup="">

如果以上方法对你不起作用,麻烦解决。

1)注意,“ datatableSetup”等同于“ dattable-setup”。

2)确保应用程序是在指令之前定义的。 例如简单的应用程序定义和指令。

var app = angular.module('app', []);
app.directive('datatableSetup', function () {
return { link: function (scope, elm, attrs) { elm.dataTable(); } }
});

如果没有提供第二个参数“延迟”,则默认行为是在 DOM 完成呈现之后执行函数。因此,不要使用 setTimeout,而是使用 $timeout:

$timeout(function () {
//DOM has finished rendering
});

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

不需要实现超时,只需要添加一个监听内容更改(甚至额外的外部触发器)的手表。

在我的例子中,我使用这个工作区在 ng-repeat 完成后初始化一个 jQuery 插件,这个插件创建了我的内部 DOM ——在另一个例子中,我使用它只是在控制器的 scope 属性改变之后操作 DOM。我是这么做的。

HTML:

<div my-directive my-directive-watch="!!myContent">\{\{myContent}}</div>

约翰逊:

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


}
}
}
}
}]);

注意: 可以想象任何任意的表达式,而不是仅仅将 myContent 变量强制转换为 bool at my-direct-watch 属性。

注意: 像上面的例子一样,每个元素只能隔离一次作用域——尝试在同一个元素上使用多个指令来做这件事会导致一个编译错误: multidir 错误——参见: < a href = “ https://docs.angularjs.org/Error/% 24edit/multidir”> https://docs.angularjs.org/Error/$compile/multidir

对我来说,没有一个解决方案是可以接受使用超时的。这是因为我使用的是在 postLink 期间动态创建的模板。

但是请注意,当超时将被调用的函数添加到浏览器队列中时,可能会有一个“0”的超时,这个超时将发生在角度渲染引擎之后,因为这个函数已经在队列中了。

请参考: http://blog.brunoscopelliti.com/run-a-directive-after-the-dom-has-finished-rendering

这里是一个指令,有行动编程后浅渲染。我所说的肤浅是指它将在渲染了那个元素之后求值,而且这个元素将与 什么时候无关,它的内容将被渲染。因此,如果你需要一些子元素做后期渲染动作,你应该考虑在那里使用它:

define(['angular'], function (angular) {
'use strict';
return angular.module('app.common.after-render', [])
.directive('afterRender', [ '$timeout', function($timeout) {
var def = {
restrict : 'A',
terminal : true,
transclude : false,
link : function(scope, element, attrs) {
if (attrs) { scope.$eval(attrs.afterRender) }
scope.$emit('onAfterRender')
}
};
return def;
}]);
});

然后你可以做:

<div after-render></div>

或者任何有用的表达,比如:

<div after-render="$emit='onAfterThisConcreteThingRendered'"></div>

回答这个问题可能会迟到,但是仍然有人会从我的回答中受益。

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

在我的控制器里:

$scope.render=false;


//this will fire after load the the page


angular.element(document).ready(function() {
$scope.render=true;
});

在我的 html 中(在我的例子中,html 组件是一个画布)

<canvas ng-if="render"> </canvas>

由于无法预测加载顺序,因此可以使用一个简单的解决方案。

让我们看看指令-“指令的用户”关系。 通常,指令的用户将向指令提供一些数据或使用指令提供的一些功能(函数)。 另一方面,指令期望在其范围内定义一些变量。

如果我们能够确保所有的玩家在他们尝试执行这些行动之前,他们的所有行动要求都得到了满足,那么一切都会好起来的。

现在是指令:

app.directive('aDirective', function () {
return {
scope: {
input: '=',
control: '='
},
link: function (scope, element) {
function functionThatNeedsInput(){
//use scope.input here
}
if ( scope.input){ //We already have input
functionThatNeedsInput();
} else {
scope.control.init = functionThatNeedsInput;
}
}


};
})

现在是 html 指令的用户

<a-directive control="control" input="input"></a-directive>

以及在使用指令的组件的控制器中的某个位置:

$scope.control = {};
...
$scope.input = 'some data could be async';
if ( $scope.control.functionThatNeedsInput){
$scope.control.functionThatNeedsInput();
}

差不多就是这样。有很多开销,但你可以失去 $超时。 我们还假设使用指令的组件在指令之前被实例化,因为我们依赖于指令被实例化时控制变量的存在。