Calling a function when ng-repeat has finished

2018-12-31 05:00发布

What I am trying to implement is basically a "on ng repeat finished rendering" handler. I am able to detect when it is done but I can't figure out how to trigger a function from it.

Check the fiddle:http://jsfiddle.net/paulocoelho/BsMqq/3/

JS

var module = angular.module('testApp', [])
    .directive('onFinishRender', function () {
    return {
        restrict: 'A',
        link: function (scope, element, attr) {
            if (scope.$last === true) {
                element.ready(function () {
                    console.log("calling:"+attr.onFinishRender);
                    // CALL TEST HERE!
                });
            }
        }
    }
});

function myC($scope) {
    $scope.ta = [1, 2, 3, 4, 5, 6];
    function test() {
        console.log("test executed");
    }
}

HTML

<div ng-app="testApp" ng-controller="myC">
    <p ng-repeat="t in ta" on-finish-render="test()">{{t}}</p>
</div>

Answer: Working fiddle from finishingmove: http://jsfiddle.net/paulocoelho/BsMqq/4/

10条回答
梦醉为红颜
2楼-- · 2018-12-31 05:22

A solution for this problem with a filtered ngRepeat could have been with Mutation events, but they are deprecated (without immediate replacement).

Then I thought of another easy one:

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

This hooks into the Node.prototype methods of the parent element with a one-tick $timeout to watch for successive modifications.

It works mostly correct but I did get some cases where the altDone would be called twice.

Again... add this directive to the parent of the ngRepeat.

查看更多
呛了眼睛熬了心
3楼-- · 2018-12-31 05:27

The other solutions will work fine on initial page load, but calling $timeout from the controller is the only way to ensure that your function is called when the model changes. Here is a working fiddle that uses $timeout. For your example it would be:

.controller('myC', function ($scope, $timeout) {
$scope.$watch("ta", function (newValue, oldValue) {
    $timeout(function () {
       test();
    });
});

ngRepeat will only evaluate a directive when the row content is new, so if you remove items from your list, onFinishRender will not fire. For example, try entering filter values in these fiddles emit.

查看更多
低头抚发
4楼-- · 2018-12-31 05:28

Please have a look at the fiddle, http://jsfiddle.net/yNXS2/. Since the directive you created didn't created a new scope i continued in the way.

$scope.test = function(){... made that happen.

查看更多
素衣白纱
5楼-- · 2018-12-31 05:32

If you’re not averse to using double-dollar scope props and you’re writing a directive whose only content is a repeat, there is a pretty simple solution (assuming you only care about the initial render). In the link function:

const dereg = scope.$watch('$$childTail.$last', last => {
    if (last) {
        dereg();
        // do yr stuff -- you may still need a $timeout here
    }
});

This is useful for cases where you have a directive that needs to do DOM manip based on the widths or heights of the members of a rendered list (which I think is the most likely reason one would ask this question), but it’s not as generic as the other solutions that have been proposed.

查看更多
登录 后发表回答