[events] ng-repeat가 끝나면 함수 호출

내가 구현하려고하는 것은 기본적으로 “반복 완료 렌더링시”처리기입니다. 언제 완료되었는지 감지 할 수는 있지만 함수를 트리거하는 방법을 알 수는 없습니다.

바이올린을 확인하십시오 : 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>

답변 : 마무리 작업에서 바이올린 작업 : http://jsfiddle.net/paulocoelho/BsMqq/4/



답변

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. $timeoutng-repeated 요소가 렌더링을 완전히 마쳤을 때 실행되는지 확인하십시오 ( $timeout현재 다이제스트주기의 끝에서 will이 실행 되기 때문에 – $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>


답변

DOM이 생성 된 후 브라우저가 렌더링되기 전에 콜백 (예 : test ())을 실행 하려면 $ evalAsync를 사용하십시오 . 이 떨림을 방지 할 수 있습니다 – 심판 .

if (scope.$last) {
   scope.$evalAsync(attr.onFinishRender);
}

깡깡이 .

렌더링 후 콜백을 실제로 호출하려면 $ timeout을 사용하십시오.

if (scope.$last) {
   $timeout(function() {
      scope.$eval(attr.onFinishRender);
   });
}

이벤트 대신 $ eval을 선호합니다. 이벤트의 경우 이벤트 이름을 알고 해당 이벤트의 컨트롤러에 코드를 추가해야합니다. $ eval을 사용하면 컨트롤러와 지시문 사이의 연결이 줄어 듭니다.


답변

지금까지 제공된 답변은 처음 ng-repeat렌더링 될 때만 작동 하지만 동적 인 경우 ng-repeat항목을 추가 / 삭제 / 필터링 할 것이고 매번 알림을 받아야 함을 의미합니다.ng-repeat 렌더링되면 해당 솔루션이 작동하지 않습니다.

따라서 처음으로뿐만 아니라 다시 렌더링을 받는다는 통지를 받아야 할ng-repeat 때, 나는 그것을 할 수있는 방법을 찾았습니다. 하기. 이 옵션을 사용 $filter하여에 ng-repeat 당신이 다른를 사용하기 전에$filter :

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

이 때마다 $emit전화 이벤트ngRepeatFinishedng-repeat 렌더링 입니다.

사용 방법:

<li ng-repeat="item in (items|ngRepeatFinish) | filter:{name:namedFiltered}" >

ngRepeatFinish필터의 요구는 직접 적용 할 Array또는은 Object으로 정의하여$scope , 당신은 후에 다른 필터를 적용 할 수 있습니다.

사용하지 않는 방법 :

<li ng-repeat="item in (items | filter:{name:namedFiltered}) | ngRepeatFinish" >

다른 필터를 먼저 적용한 다음 필터를 적용하지 마십시오 ngRepeatFinish.

언제 사용해야합니까?

목록 렌더링이 완료된 후 특정 CSS 스타일을 DOM에 적용하려는 경우 다음에 의해 다시 렌더링 된 DOM 요소의 새로운 차원을 고려해야합니다. ng-repeat . (BTW : 이러한 종류의 작업은 지시문 내에서 수행해야합니다)

ngRepeatFinished이벤트 를 처리하는 함수에서 수행하지 말아야 할 것 :

  • $scope.$apply해당 함수에서을 수행하지 마십시오. 그렇지 않으면 Angular가 감지 할 수없는 무한 루프에 Angular를 배치하게됩니다.

  • $scope속성 을 변경하는 데 사용하지 마십시오. 변경 사항은 다음 $digest루프 까지보기에 반영 되지 않으며 수행 할 수 없으므로 사용 $scope.$apply하지 않습니다.

“그러나 필터는 그런 식으로 사용되지 않습니다 !!”

아니, 그들은, 그것은 당신이 그것을 사용하지 않는 것을 좋아하지 않는 경우, 해킹입니다. 같은 것을 성취하는 더 좋은 방법을 알고 있다면 알려주십시오.

요약

이것은 해킹 이며 잘못된 방식으로 사용하는 것은 위험하므로 ng-repeat렌더링이 완료된 후에 스타일을 적용하는 데만 사용하면 아무런 문제가 없습니다.


답변

동일한 컨트롤러에서 다른 ng-repeats에 대해 다른 함수를 호출 해야하는 경우 다음과 같이 시도 할 수 있습니다.

지시어 :

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

컨트롤러에서 $ on으로 이벤트를 잡으십시오.

$scope.$on('ngRepeatBroadcast1', function(ngRepeatFinishedEvent) {
// Do something
});

$scope.$on('ngRepeatBroadcast2', function(ngRepeatFinishedEvent) {
// Do something
});

여러 번 반복되는 템플릿에서

<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>


답변

다른 솔루션은 초기 페이지로드에서 제대로 작동하지만 컨트롤러에서 $ timeout을 호출하는 것이 모델이 변경 될 때 함수가 호출되도록하는 유일한 방법입니다. $ timeout을 사용 하는 작동하는 바이올린 은 다음과 같습니다 . 귀하의 예를 들면 다음과 같습니다.

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

ngRepeat는 행 내용이 새로운 경우에만 지시문을 평가하므로 목록에서 항목을 제거하면 onFinishRender가 실행되지 않습니다. 예를 들어, 이러한 바이올린 방출 에 필터 값을 입력하십시오 .


답변

이중 달러 범위 소품을 사용하지 않고 내용 만 반복되는 지시문을 작성하는 경우 매우 간단한 해결책이 있습니다 (초기 렌더링에만 관심이 있다고 가정). 링크 기능에서 :

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

이것은 렌더링 된 목록의 멤버의 너비 또는 높이를 기반으로 DOM 조작을 수행 해야하는 지시문이있는 경우에 유용하지만 (이 질문을 할 가능성이 가장 큰 이유라고 생각합니다) 제안 된 다른 솔루션으로.


답변

필터링 된 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');
            }
        }
    };
});

이것은 한 번의 $ timeout으로 부모 요소의 Node.prototype 메소드에 연결되어 연속적인 수정을 감시합니다.

대부분 올바르게 작동하지만 altDone이 두 번 호출되는 경우가 있습니다.

다시 …이 지시어를 ngRepeat 의 부모 에 추가하십시오 .