Events ng repeat完成时调用函数
我试图实现的基本上是一个“on ng repeat finished rendering”处理程序。我能够检测何时完成,但我不知道如何从中触发函数 检查小提琴: JS HTMLEvents ng repeat完成时调用函数,events,angularjs,handler,directive,ready,Events,Angularjs,Handler,Directive,Ready,我试图实现的基本上是一个“on ng repeat finished rendering”处理程序。我能够检测何时完成,但我不知道如何从中触发函数 检查小提琴: JS HTML 在finish render=“test()”>{{t} 回答: 完成后的工作小提琴移动:请看一下小提琴。因为您创建的指令没有创建新的作用域,所以我继续这样做 $scope.test=function(){…实现了这一点 var module = angular.module('testApp', []) .
在finish render=“test()”>{{t}
回答:
完成后的工作小提琴移动:请看一下小提琴。因为您创建的指令没有创建新的作用域,所以我继续这样做
$scope.test=function(){…
实现了这一点
var module = angular.module('testApp', [])
.directive('onFinishRender', function ($timeout) {
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last === true) {
$timeout(function () {
scope.$emit(attr.onFinishRender);
});
}
}
}
});
请注意,我没有使用.ready()
,而是将其包装在$timeout
$timeout
中,以确保在ng重复元素真正完成渲染时执行它(因为$timeout
将在当前摘要周期结束时执行--,并且它还将在内部调用$apply
,这与setTimeout
不同)。因此在ng repeat
完成后,我们使用$emit
将事件发送到外部作用域(同级和父级作用域)
然后在控制器中,您可以使用$on
捕捉它:
$scope.$on('ngRepeatFinished', function(ngRepeatFinishedEvent) {
//you also get the actual event object
//do stuff, execute functions -- whatever...
});
使用类似以下内容的html:
<div ng-repeat="item in items" on-finish-render="ngRepeatFinished">
<div>{{item.name}}}<div>
</div>
{{item.name}}
如果您希望在构建DOM之后但在浏览器呈现之前执行回调(即test()),请使用。这将防止闪烁--
如果确实要在渲染后调用回调,请使用$timeout:
if (scope.$last) {
$timeout(function() {
scope.$eval(attr.onFinishRender);
});
}
我更喜欢$eval而不是事件。对于事件,我们需要知道事件的名称并为该事件向控制器添加代码。对于$eval,控制器和指令之间的耦合更少。其他解决方案在初始页面加载时可以正常工作,但从控制器调用$timeout是确保当模型更改时调用函数。下面是一个使用$timeout的工作。例如:
.controller('myC', function ($scope, $timeout) {
$scope.$watch("ta", function (newValue, oldValue) {
$timeout(function () {
test();
});
});
<div ng-app="testApp" ng-controller="myC">
<p ng-repeat="t in ta" ng-init="$last && test()">{{t}}</p>
</div>
ngRepeat仅在行内容为新内容时对指令求值,因此如果从列表中删除项,则不会触发onFinishRender。例如,尝试在这些小提琴中输入筛选值。到目前为止给出的答案仅在第一次呈现
ng repeat
时有效,但如果您有一个动态的ng repeat
,这意味着您将要添加/删除/筛选项目,并且每次呈现ng repeat
时都需要得到通知,这些解决方案将不适用于您
因此,如果您需要在每次重新呈现ng repeat
时得到通知,而不仅仅是第一次,我已经找到了一种方法,它非常“黑客”,但是如果您知道自己在做什么,它会很好地工作。在使用任何其他$filte>之前,在ng repeat
中使用此$filter
r
:
.filter('ngRepeatFinish', function($timeout){
return function(data){
var me = this;
var flagProperty = '__finishedRendering__';
if(!data[flagProperty]){
Object.defineProperty(
data,
flagProperty,
{enumerable:false, configurable:true, writable: false, value:{}});
$timeout(function(){
delete data[flagProperty];
me.$emit('ngRepeatFinished');
},0,false);
}
return data;
};
})
每次渲染ng repeat
时,这将$emit
一个名为ngRepeatFinished
的事件
如何使用它:
不要先应用其他过滤器,然后再应用ngRepeatFinish
过滤器
我什么时候用这个?
如果您希望在列表完成渲染后将某些css样式应用到DOM中,因为您需要考虑由ng repeat
重新渲染的DOM元素的新维度(顺便说一句:此类操作应在指令内完成)
在处理ngRepeatFinished
事件的函数中不应执行的操作:
- 不要在该函数中执行
,否则您会将Angular放入Angular无法检测到的无尽循环中$scope.$apply
- 不要使用它在
属性中进行更改,因为这些更改直到下一个$scope
循环才会反映在视图中,并且因为您无法执行$digest
所以它们没有任何用处$scope.$apply
ng repeat
完成渲染后应用样式时使用它,您不应该有任何问题
使用过滤的ngRepeat解决此问题的方法可能是使用事件,但它们已被弃用(无需立即替换) 然后我想到了另一个简单的方法:
app.directive('filtered',function($timeout) {
return {
restrict: 'A',link: function (scope,element,attr) {
var elm = element[0]
,nodePrototype = Node.prototype
,timeout
,slice = Array.prototype.slice
;
elm.insertBefore = alt.bind(null,nodePrototype.insertBefore);
elm.removeChild = alt.bind(null,nodePrototype.removeChild);
function alt(fn){
fn.apply(elm,slice.call(arguments,1));
timeout&&$timeout.cancel(timeout);
timeout = $timeout(altDone);
}
function altDone(){
timeout = null;
console.log('Filtered! ...fire an event or something');
}
}
};
});
这将通过一个tick$timeout钩住父元素的Node.prototype方法,以监视后续修改
它的工作原理基本上是正确的,但我确实遇到过一些情况,altDone会被调用两次
再次…将此指令添加到ngRepeat的父级。如果需要为同一控制器上的不同ng Repeat调用不同的函数,可以尝试以下操作:
<div ng-repeat="item in items" on-finish-render="ngRepeatFinished">
<div>{{item.name}}}<div>
</div>
指令:
var module = angular.module('testApp', [])
.directive('onFinishRender', function ($timeout) {
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last === true) {
$timeout(function () {
scope.$emit(attr.broadcasteventname ? attr.broadcasteventname : 'ngRepeatFinished');
});
}
}
}
});
$scope.$on('ngRepeatBroadcast1', function(ngRepeatFinishedEvent) {
// Do something
});
$scope.$on('ngRepeatBroadcast2', function(ngRepeatFinishedEvent) {
// Do something
});
在控制器中,捕捉$on的事件:
var module = angular.module('testApp', [])
.directive('onFinishRender', function ($timeout) {
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last === true) {
$timeout(function () {
scope.$emit(attr.broadcasteventname ? attr.broadcasteventname : 'ngRepeatFinished');
});
}
}
}
});
$scope.$on('ngRepeatBroadcast1', function(ngRepeatFinishedEvent) {
// Do something
});
$scope.$on('ngRepeatBroadcast2', function(ngRepeatFinishedEvent) {
// Do something
});
在模板中使用多个ng repeat
<div ng-repeat="item in collection1" on-finish-render broadcasteventname="ngRepeatBroadcast1">
<div>{{item.name}}}<div>
</div>
<div ng-repeat="item in collection2" on-finish-render broadcasteventname="ngRepeatBroadcast2">
<div>{{item.name}}}<div>
</div>
{{item.name}}
{{item.name}}
非常简单,我就是这样做的
指令('blockOnRender',函数($blockUI){
返回{
限制:“A”,
链接:函数(范围、元素、属性){
如果(范围$first){
$blockUI.blockElement($(element.parent());
}
如果(范围$last){
$blockUI.unblockElement($(element.parent());
}
}
};
})
如果您不反对使用双美元范围道具,并且您编写的指令的唯一内容是重复,那么有一个非常简单的解决方案(假设您只关心初始值)
<div ng-repeat="item in collection1" on-finish-render broadcasteventname="ngRepeatBroadcast1">
<div>{{item.name}}}<div>
</div>
<div ng-repeat="item in collection2" on-finish-render broadcasteventname="ngRepeatBroadcast2">
<div>{{item.name}}}<div>
</div>
const dereg = scope.$watch('$$childTail.$last', last => {
if (last) {
dereg();
// do yr stuff -- you may still need a $timeout here
}
});
ng-init="$last && test()"
<div ng-app="testApp" ng-controller="myC">
<p ng-repeat="t in ta" ng-init="$last && test()">{{t}}</p>
</div>
$scope.test = function test() {
console.log("test executed");
}