AngularJS:如何在AngularJS渲染模板之后运行其他代码?

我在DOM中有一个Angular模板。 当我的控制器从服务中获取新数据时,它会更新$ scope中的模型,并重新呈现模板。 迄今为止都很好。

问题是我需要在模板被重新渲染并且在DOM(在这个例子中是一个jQuery插件)之后做一些额外的工作。

似乎应该有一个事件来听,如AfterRender,但我找不到任何这样的事情。 也许一个指令将是一条路要走,但似乎也太火了。

这是一个jsFiddle概述我的问题: 小提琴AngularIssue

==更新==

基于有用的评论,我已经相应地切换到处理DOM操作的指令,并在指令中实现了一个模型$ watch。 不过,我仍然有相同的基础问题; $ watch事件中的代码在模板被编译并插入到DOM之前触发,因此,jquery插件总是评估一个空表。

有趣的是,如果我删除asynchronous调用,整个事情就可以正常工作,所以这是一个正确的方向。

这是我更新的小提琴,以反映这些变化: http : //jsfiddle.net/uNREn/12/

这篇文章是旧的,但我改变你的代码:

scope.$watch("assignments", function (value) {//I change here var val = value || null; if (val) element.dataTable({"bDestroy": true}); }); } 

请参阅: http : //jsfiddle.net/dnzY9/

我希望它能帮助你

首先,渲染的正确位置是指令。 我的build议是包装DOM操纵jQuery插件像这样的指令。

我有同样的问题,并提出这个片段。 它使用$watch$evalAsync来确保您的代码在像ng-repeat这样的指令被parsing并且像{{ value }}类的模板被渲染之后运行。

 app.directive('name', function() { return { link: function($scope, element, attrs) { // Trigger when number of children changes, // including by directives like ng-repeat var watch = $scope.$watch(function() { return element.children().length; }, function() { // Wait for templates to render $scope.$evalAsync(function() { // Finally, directives are evaluated // and templates are renderer here var children = element.children(); console.log(children); }); }); }, }; }); 

希望这可以帮助你防止一些斗争。

遵循Misko的build议,如果你想asynchronous操作,那么而不是$超时()(这是行不通的)

 $timeout(function () { $scope.assignmentsLoaded(data); }, 1000); 

使用$ evalAsync()(它工作)

 $scope.$evalAsync(function() { $scope.assignmentsLoaded(data); } ); 

小提琴 。 我还添加了一个“删除数据行”链接,该链接将修改$ scope.assignments,模拟对数据/模型的更改 – 以显示更改数据的工作方式。

概念性概述页面的运行时部分解释了当您需要在当前堆栈框架之外,但在浏览器呈现之前发生的情况时,应使用evalAsync。 (这里猜测…“当前堆栈框架”可能包括Angular DOM更新。)如果在浏览器呈现之后需要某些事情发生,请使用$ timeout。

然而,正如你已经发现的,我不认为这里需要asynchronous操作。

我发现最简单的(廉价而愉快的)解决scheme是简单地在最后一个元素的末尾添加一个空的span-ng = show =“someFunctionThatAlwaysReturnsZeroOrNothing()”。 此function将在检查是否显示span元素时运行。 执行此function中的任何其他代码。

我意识到这不是最优雅的做事方式,但是,它适用于我…

我也遇到了类似的情况,虽然稍微颠倒了一些,但是在animation开始的时候我需要删除一个加载指示器,在移动设备上,angular色的初始化速度要比要显示的animation快得多,并且使用ng-cloak是不够的,因为加载指示器在显示任何实际数据之前就已经移除了。 在这种情况下,我只是将我的返回0函数添加到第一个呈现的元素,并在该函数翻转隐藏加载指标的变种。 (当然我在这个函数触发的加载指针中加了一个ng-hide。

最后,我find了解决scheme,我正在使用REST服务来更新我的collections。 为了转换数据表jquery是以下代码:

 $scope.$watchCollection( 'conferences', function( old, nuew ) { if( old === nuew ) return; $( '#dataTablex' ).dataTable().fnDestroy(); $timeout(function () { $( '#dataTablex' ).dataTable(); }); }); 

我不得不经常这样做。 我有一个指令,需要做一些jquery的东西,模型的东西完全加载到DOM后。 所以我把我的逻辑在指令的链接:函数,并在setTimeout(函数(){…..},1); 在DOM被加载之后,setTimout将被触发,并且在代码被执行之前DOM加载之后的最短时间为1毫秒。 这似乎为我工作,但我希望angular提出了一个事件,一旦完成模板加载,使该模板使用的指令可以做jQuery的东西,并访问DOM元素。 希望这可以帮助。

您也可以创build一个指令,在链接函数中运行您的代码。

看到这个stackoverflow答复 。

$ scope。$ evalAsync()或者$ timeout(fn,0)对于我来说都是可靠的。

我必须把两者结合起来。 我做了一个指令,并且把一个优先级高于缺省值的优先级设为高。 这是一个指令(注意我使用ngInject注入依赖项):

 app.directive('postrenderAction', postrenderAction); /* @ngInject */ function postrenderAction($timeout) { // ### Directive Interface // Defines base properties for the directive. var directive = { restrict: 'A', priority: 101, link: link }; return directive; // ### Link Function // Provides functionality for the directive during the DOM building/data binding stage. function link(scope, element, attrs) { $timeout(function() { scope.$evalAsync(attrs.postrenderAction); }, 0); } } 

要调用该指令,你可以这样做:

 <div postrender-action="functionToRun()"></div> 

如果你想在ng-repeat完成后调用它,我在ng-repeat和ng-if =“$ last”中添加了一个空的span:

 <li ng-repeat="item in list"> <!-- Do stuff with list --> ... <!-- Fire function after the last element is rendered --> <span ng-if="$last" postrender-action="$ctrl.postRender()"></span> </li> 

我带来了一个非常简单的解决scheme。 我不确定这是否是正确的做法,但是它在实践中起作用。 让我们直接看看我们想要渲染的东西。 例如,在包含一些ng-repeat的指令中,我会注意段落或整个html的文本长度(可能还有其他的东西!)。 该指令将是这样的:

 .directive('myDirective', [function () { 'use strict'; return { link: function (scope, element, attrs) { scope.$watch(function(){ var whole_p_length = 0; var ps = element.find('p'); for (var i=0;i<ps.length;i++){ if (ps[i].innerHTML == undefined){ continue } whole_p_length+= ps[i].innerHTML.length; } //it could be this too: whole_p_length = element[0].innerHTML.length; but my test showed that the above method is a bit faster console.log(whole_p_length); return whole_p_length; }, function (value) { //Code you want to be run after rendering changes }); } }]); 

请注意 ,代码实际上是在渲染更改而非完整渲染之后运行的 但是我想在大多数情况下,只要渲染发生变化就可以处理这种情况。 如果你想在渲染完成后只运行你的代码一次 ,你也可以考虑比较这个长度(或其他任何度量)与你的模型。 我很欣赏对此的任何想法/评论。

你可以使用angular-ui utils的'jQuery Passthrough'模块。 我成功地将一个jQuery touch carousel插件绑定到了一些图像,我从Web服务中检索asynchronous,并使用ng-repeat来呈现它们。

在更新服务并redirect到新视图(页面),然后在更新服务之前加载指令的情况下,如果$ watch或$ timeout失败,则可以使用$ rootScope。$ broadcast

视图

 <service-history log="log" data-ng-repeat="log in requiedData"></service-history> 

调节器

 app.controller("MyController",['$scope','$rootScope', function($scope, $rootScope) { $scope.$on('$viewContentLoaded', function () { SomeSerive.getHistory().then(function(data) { $scope.requiedData = data; $rootScope.$broadcast("history-updation"); }); }); }]); 

指示

 app.directive("serviceHistory", function() { return { restrict: 'E', replace: true, scope: { log: '=' }, link: function($scope, element, attrs) { function updateHistory() { if(log) { //do something } } $rootScope.$on("history-updation", updateHistory); } }; });