249

私が実装しようとしているのは、基本的に「オン ng 繰り返し終了レンダリング」ハンドラーです。いつ完了したかを検出できますが、そこから関数をトリガーする方法がわかりません。

フィドルを確認してください: 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>

回答:finishmove の作業フィドル: http://jsfiddle.net/paulocoelho/BsMqq/4/

4

10 に答える 10

400
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は現在のダイジェスト サイクルの最後に実行され、とは異なり、内部的にも呼び出さ$applysetTimeoutれるため)。そのため、終了後、イベントを外側のスコープ (兄弟スコープと親スコープ) に発行するため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>
于 2013-03-04T18:23:51.913 に答える
89

DOMが構築された後、ブラウザーがレンダリングされる前にコールバック(つまり、test())を実行する場合は、$evalAsyncを使用します。これにより、ちらつきが防止されます--ref

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

フィドル

レンダリング後にコールバックを本当に呼び出したい場合は、$timeoutを使用します。

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

私はイベントよりも$evalの方が好きです。イベントでは、イベントの名前を知り、そのイベントのコントローラーにコードを追加する必要があります。$ evalを使用すると、コントローラーとディレクティブ間の結合が少なくなります。

于 2013-03-04T19:19:09.407 に答える
4

2 倍のスコープ props を使用することを嫌がらず、内容が繰り返しのみのディレクティブを作成している場合は、非常に簡単な解決策があります (最初のレンダリングのみを気にする場合)。リンク機能では:

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

これは、レンダリングされたリストのメンバーの幅または高さに基づいて DOM 操作を行う必要があるディレクティブがある場合に役立ちます (これが、この質問をする最も可能性の高い理由だと思います) が、それほど一般的ではありません。提案された他の解決策として。

于 2016-09-26T03:53:03.680 に答える
1

フィルタリングされた ngRepeat を使用したこの問題の解決策は、Mutationイベントを使用することでした。

それから私は別の簡単なものを考えました:

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

これは、親要素の Node.prototype メソッドに 1 回の $timeout を指定してフックし、連続する変更を監視します。

それはほとんど正しく動作しますが、altDone が 2 回呼び出されるケースがいくつかありました。

繰り返しますが、このディレクティブをngRepeatの親に追加してください。

于 2015-04-22T08:57:13.523 に答える
0

フィドルhttp://jsfiddle.net/yNXS2/をご覧ください。あなたが作成したディレクティブは新しいスコープを作成しなかったので、私は途中で続けました。

$scope.test = function(){...それを実現させました。

于 2013-03-04T18:10:18.467 に答える