有没有对Angular JS指令的post rendercallback?
我刚刚得到我的指令拉入模板来追加到它的元素是这样的:
# 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_items AFTER DOM准备好dataTable()。 上面的指令调用dataTable()方法之前模板被附加到指令的元素。 有没有办法,我可以调用后附加function?
感谢您的帮助!
更新1后安迪的回答:
我想确保链接方法只会被调用AFTER一切都在页面上,所以我改变了一个小testing指令:
# 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') %>" }
那是有效的。 所以我想知道在非定时器版本的代码中出了什么问题?
如果没有提供第二个参数“delay”,则默认行为是在DOM完成渲染后执行该函数。 所以,而不是setTimeout,使用$超时:
$timeout(function () { //DOM has finished rendering });
我有同样的问题,我相信答案真的不是。 请参阅Miško的评论和小组中的一些讨论 。
Angular可以跟踪它所做的操作DOM的所有函数调用是完整的,但是由于这些函数可能会触发在返回之后仍然更新DOM的asynchronous逻辑,所以Angular不能期望知道它。 Angular提供的任何callback函数有时可能会起作用,但依赖于它并不安全。
我们用setTimeout解决了这个问题,就像你一样。
(请记住,不是每个人都同意我的观点 – 你应该阅读上面的链接的意见,看看你的想法。)
您可以使用在链接模板后运行的“链接”function,也称为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
尽pipe我的答案与数据表无关,但它解决了DOM操作的问题,例如jQuery插件初始化用于以asynchronous方式更新内容的元素上使用的指令。
而不是实现一个超时,可以添加一个手表,将监听内容的变化(甚至额外的外部触发器)。
在我的情况下,我使用这个解决方法来初始化一个jQuery插件,一旦ng-repeat被创build了我的内部DOM – 在另一种情况下,我用它来在控制器改变scope属性后操作DOM。 这是我做的…
HTML:
<div my-directive my-directive-watch="!!myContent">{{myContent}}</div>
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 ... } }); } } } } }]);
注意:不要只在my-directive-watch属性上将myContentvariables转换为bool,我们可以想象任何expression式。
注意:像上面的例子那样分离范围只能每个元素执行一次 – 试图在同一个元素上使用多个指令来做到这一点,将导致$ compile:multidir错误 – 请参阅: https : //docs.angularjs.org /错误/ $编译/ multidir
可能迟到回答这个问题。 但是,仍然有人可以从我的答案中获益。
我有类似的问题,在我的情况下,我不能改变指令,因为它是一个图书馆,改变图书馆的代码是不是一个好的做法。 所以我所做的是使用一个variables来等待页面加载,并使用ng – 如果在我的html内部等待渲染特定的元素。
在我的控制器中:
$scope.render=false; //this will fire after load the the page angular.element(document).ready(function() { $scope.render=true; });
在我的HTML(在我的情况下HTML组件是一个canvas)
<canvas ng-if="render"> </canvas>
我有同样的问题,但使用Angular + DataTable与fnDrawCallback
+ 行分组 + $编译嵌套指令。 我把$超时放在我的fnDrawCallback
函数来修复分页渲染。
在举例之前,根据row_grouping来源:
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指令。
没有解决scheme为我工作接受使用超时。 这是因为我使用的是在postLink中dynamic创build的模板。
但是请注意,可能会有超时值为0,因为超时将被调用的函数添加到angular色呈现引擎之后发生的浏览器队列中,因为这已经在队列中了。
参考这个: 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>
或者与任何有用的expression式一样:
<div after-render="$emit='onAfterThisConcreteThingRendered'"></div>
我得到这个工作与以下指令:
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'相当于'datatable-setup'。 Angular将格式转换为骆驼的情况。
2)确保应用程序在指令之前定义。 如简单的应用程序定义和指令。
var app = angular.module('app', []); app.directive('datatableSetup', function () { return { link: function (scope, elm, attrs) { elm.dataTable(); } } });
在加载顺序不能被预期的情况下,可以使用简单的解决scheme。
让我们看看指令'用户指令'的关系。 通常,指令的用户将向指令提供一些数据或使用指令提供的一些function(function)。 另一方面,指令期望在其范围上定义一些variables。
如果我们能够确保所有参与者在尝试执行这些行动之前都已经完成了所有的行动要求,那么一切都应该很好。
现在的指令是:
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(); }
就是这样。 有很多的开销,但你可以失去$超时。 我们还假设使用指令的组件在指令之前被实例化,因为当指令被实例化时,我们依赖于控制variables的存在。