Ng-repeat结束事件

我想调用一些jQuery函数针对div表。该表由ng-repeat填充。

当我打开它的时候

$(document).ready()

我没有结果。

$scope.$on('$viewContentLoaded', myFunc);

没有帮助。

是否有办法在ng-repeat填充完成后立即执行函数?我读了一个关于使用自定义directive的建议,但我不知道如何使用ng-repeat和我的div…

209938 次浏览

实际上,您应该使用指令,并且ng-Repeat循环的末尾没有绑定事件(因为每个元素都是单独构造的,并且有自己的事件)。但是a)使用指令可能是你所需要的,b)有一些ng-Repeat特定的属性,你可以使用它来创建你的“on ngRepeat finished”事件。

具体来说,如果你想要的只是在整个表中添加事件样式,你可以使用包含所有ngRepeat元素的指令来实现。另一方面,如果你想具体地处理每个元素,你可以在ngRepeat中使用一个指令,它将在每个元素被创建后作用于每个元素。

然后,你可以使用$index$first$middle$last属性来触发事件。对于这个HTML:

<div ng-controller="Ctrl" my-main-directive>
<div ng-repeat="thing in things" my-repeat-directive>
thing \{\{thing}}
</div>
</div>

你可以像这样使用指令:

angular.module('myApp', [])
.directive('myRepeatDirective', function() {
return function(scope, element, attrs) {
angular.element(element).css('color','blue');
if (scope.$last){
window.alert("im the last!");
}
};
})
.directive('myMainDirective', function() {
return function(scope, element, attrs) {
angular.element(element).css('border','5px solid red');
};
});

砰砰作响中查看它的实际操作。希望能有所帮助!

如果你只是想在循环的末尾执行一些代码,这里有一个稍微简单的变体,不需要额外的事件处理:

<div ng-controller="Ctrl">
<div class="thing" ng-repeat="thing in things" my-post-repeat-directive>
thing \{\{thing}}
</div>
</div>
function Ctrl($scope) {
$scope.things = [
'A', 'B', 'C'
];
}


angular.module('myApp', [])
.directive('myPostRepeatDirective', function() {
return function(scope, element, attrs) {
if (scope.$last){
// iteration is complete, do whatever post-processing
// is necessary
element.parent().css('border', '1px solid black');
}
};
});

< a href = " http://plnkr。co/edit/icWaAW">现场演示。 . co/edit/icWaAW

这里是一个重复完成指令,当为真时调用指定的函数。我发现被调用的函数在做DOM操作之前必须使用interval=0的$timeout,比如在呈现的元素上初始化工具提示。jsFiddle: http://jsfiddle.net/tQw6w/

在美元范围。layoutDone,试着注释掉$timeout行和取消注释“NOT CORRECT!”行,看看工具提示中的区别。

<ul>
<li ng-repeat="feed in feedList" repeat-done="layoutDone()" ng-cloak>
<a href="\{\{feed}}" title="view at \{\{feed | hostName}}" data-toggle="tooltip">\{\{feed | strip_http}}</a>
</li>
</ul>

JS:

angular.module('Repeat_Demo', [])


.directive('repeatDone', function() {
return function(scope, element, attrs) {
if (scope.$last) { // all are rendered
scope.$eval(attrs.repeatDone);
}
}
})


.filter('strip_http', function() {
return function(str) {
var http = "http://";
return (str.indexOf(http) == 0) ? str.substr(http.length) : str;
}
})


.filter('hostName', function() {
return function(str) {
var urlParser = document.createElement('a');
urlParser.href = str;
return urlParser.hostname;
}
})


.controller('AppCtrl', function($scope, $timeout) {


$scope.feedList = [
'http://feeds.feedburner.com/TEDTalks_video',
'http://feeds.nationalgeographic.com/ng/photography/photo-of-the-day/',
'http://sfbay.craigslist.org/eng/index.rss',
'http://www.slate.com/blogs/trending.fulltext.all.10.rss',
'http://feeds.current.com/homepage/en_US.rss',
'http://feeds.current.com/items/popular.rss',
'http://www.nytimes.com/services/xml/rss/nyt/HomePage.xml'
];


$scope.layoutDone = function() {
//$('a[data-toggle="tooltip"]').tooltip(); // NOT CORRECT!
$timeout(function() { $('a[data-toggle="tooltip"]').tooltip(); }, 0); // wait...
}


})

当你检查scope.$last变量来用setTimeout(someFn, 0)包装你的触发器时,它也可能是必要的。setTimeout 0是javascript中公认的技术,它是我的directive正确运行的必要条件。

这里有一个使用ng-init的简单方法,甚至不需要自定义指令。在某些情况下,它对我来说工作得很好,例如,需要在页面加载时自动滚动ng-repeat项的div到特定项,因此滚动函数需要等待,直到ng-repeat完成对DOM的渲染才可以触发。

<div ng-controller="MyCtrl">
<div ng-repeat="thing in things">
thing: \{\{ thing }}
</div>
<div ng-init="fireEvent()"></div>
</div>

myModule.controller('MyCtrl', function($scope, $timeout){
$scope.things = ['A', 'B', 'C'];


$scope.fireEvent = function(){


// This will only run after the ng-repeat has rendered its things to the DOM
$timeout(function(){
$scope.$broadcast('thingsRendered');
}, 0);


};
});

注意,这只对ng-repeat初始渲染后需要调用一次的函数有用。如果你需要在ng-repeat内容更新时调用一个函数,那么你必须在这个线程上使用一个自定义指令的其他答案之一。

也许使用ngInit和Lodash的debounce方法更简单一点,不需要自定义指令:

控制器:

$scope.items = [1, 2, 3, 4];


$scope.refresh = _.debounce(function() {
// Debounce has timeout and prevents multiple calls, so this will be called
// once the iteration finishes
console.log('we are done');
}, 0);

模板:

<ul>
<li ng-repeat="item in items" ng-init="refresh()">\{\{item}}</li>
</ul>

更新

还有一个使用三元运算符的更简单的纯AngularJS解决方案:

模板:

<ul>
<li ng-repeat="item in items" ng-init="$last ? doSomething() : null">\{\{item}}</li>
</ul>

注意ngInit使用预链接编译阶段——即在处理子指令之前调用表达式。这意味着仍然需要异步处理。

这是对其他回答中表达的想法的改进,以展示如何在使用声明性语法和使用element-directive隔离作用域(谷歌推荐的最佳实践)时访问ngRepeat属性($index, $first, $middle, $last, $even, $odd)。注意主要的区别:scope.$parent.$last

angular.module('myApp', [])
.directive('myRepeatDirective', function() {
return {
restrict: 'E',
scope: {
someAttr: '='
},
link: function(scope, element, attrs) {
angular.element(element).css('color','blue');
if (scope.$parent.$last){
window.alert("im the last!");
}
}
};
});

如果你只是想改变类名,让它以不同的方式呈现,下面的代码可以做到这一点。

<div>
<div ng-show="loginsuccess" ng-repeat="i in itemList">
<div id="\{\{i.status}}" class="\{\{i.status}}">
<div class="listitems">\{\{i.item}}</div>
<div class="listitems">\{\{i.qty}}</div>
<div class="listitems">\{\{i.date}}</div>
<div class="listbutton">
<button ng-click="UpdateStatus(i.$id)" class="btn"><span>Done</span></button>
<button ng-click="changeClass()" class="btn"><span>Remove</span></button>
</div>
<hr>
</div>

这段代码适用于我,当我有一个类似的需求,以strick槽字体呈现购物清单中的购物项目时。

补充Pavel的回答,一些更易读和容易理解的东西将是:

<ul>
<li ng-repeat="item in items"
ng-init="$last ? doSomething() : angular.noop()">\{\{item}}</li>
</ul>

为什么你认为angular.noop在第一个地方…?

优点:

你不需要为此写一个指令……

我想添加另一个答案,因为前面的答案需要在ngRepeat完成后运行的代码是一个angular代码,在这种情况下,所有的答案都给出了一个伟大而简单的解决方案,有些比其他的更通用,如果它的重要的摘要生命周期阶段,你可以看看本·纳德尔的博客关于它,除了使用$parse而不是$eval。

但根据我的经验,OP状态,它通常在最终编译的DOM上运行一些JQuery插件或方法,在这种情况下,我发现最简单的解决方案是创建一个带有setTimeout的指令,因为setTimeout函数被推到浏览器队列的末尾,它总是在angular中所有事情都完成之后,通常是ngReapet,它继续在它的父母postLinking函数之后

angular.module('myApp', [])
.directive('pluginNameOrWhatever', function() {
return function(scope, element, attrs) {
setTimeout(function doWork(){
//jquery code and plugins
}, 0);
};
});

对于那些想知道在这种情况下为什么不使用$timeout的人来说,它会导致另一个完全不必要的消化周期

我是这样做的。

创建指令

function finRepeat() {
return function(scope, element, attrs) {
if (scope.$last){
// Here is where already executes the jquery
$(document).ready(function(){
$('.materialboxed').materialbox();
$('.tooltipped').tooltip({delay: 50});
});
}
}
}


angular
.module("app")
.directive("finRepeat", finRepeat);

在你把它添加到这个ng-repeat的标签上之后

<ul>
<li ng-repeat="(key, value) in data" fin-repeat> \{\{ value }} </li>
</ul>

然后在ng-repeat结束时运行。

没有必要创建一个指令,特别是为了有一个ng-repeat完成事件。

ng-init为你做的魔法。

  <div ng-repeat="thing in things" ng-init="$last && finished()">

$last确保finished只在最后一个元素被呈现给DOM时才被触发。

不要忘记创建$scope.finished事件。

编码快乐! !

编辑:2016年10月23日

如果你还想在数组中没有项时调用finished函数,那么你可以使用下面的解决方案

<div style="display:none" ng-init="things.length < 1 && finished()"></div>
//or
<div ng-if="things.length > 0" ng-init="finished()"></div>

只需在ng-repeat元素的顶部添加上面的行。它将检查数组是否没有任何值,并相应地调用函数。

如。

<div ng-if="things.length > 0" ng-init="finished()"></div>
<div ng-repeat="thing in things" ng-init="$last && finished()">

我必须在ng-repeat结束后使用MathJax渲染公式,上面的答案都没有解决我的问题,所以我做了如下。它是这不是一个很好的解决方案,但对我有用…

<div ng-repeat="formula in controller.formulas">
<div>\{\{formula.string}}</div>
\{\{$last ? controller.render_formulas() : ""}}
</div>
<div ng-repeat="i in items">
<label>\{\{i.Name}}</label>
<div ng-if="$last" ng-init="ngRepeatFinished()"></div>
</div>

我的解决方案是添加一个div来调用一个函数,如果项目是repeat中的最后一个。

我找到了一个答案在这里很熟练,但仍然有必要添加一个延迟

创建以下指令:

angular.module('MyApp').directive('emitLastRepeaterElement', function() {
return function(scope) {
if (scope.$last){
scope.$emit('LastRepeaterElement');
}
}; });

将它作为一个属性添加到你的中继器中,像这样:

<div ng-repeat="item in items" emit-last-repeater-element></div>

根据Radu的说法:

$scope.eventoSelecionado.internamento_evolucoes.forEach(ie => {mycode});

对我来说,它是有效的,但我仍然需要添加一个setTimeout

$scope.eventoSelecionado.internamento_evolucoes.forEach(ie => {
setTimeout(function() {
mycode
}, 100); });