Как вы можете обнаружить, когда рендеринг HTML завершен в AngularJS

Я провел обширные исследования по этой теме, но независимо от того, что я делаю, мне очень сложно достичь этой цели.

Я хочу выполнить код, когда все элементы были полностью отображены в веб-приложении AngularJS. Я думаю, что нашел решение, предлагающее использовать маршрутизаторы и представления, но я не мог сделать эту работу над своим делом, поскольку, похоже, для этого требуется определенная конфигурация.

Когда у вас есть ng-repeat и много вложенных directives, которые будут генерировать HTML/Content на основе различных условий, используя ng-if, я заметил, что рендеринг HTML продолжается даже после того, как событие готовности документа или просмотр содержимого был загружено событие $viewContentLoaded.

Ближайшей идеей, которую я имею, является использование $watch по длине дочерних элементов элемента данного directive. Каждый раз, когда выполняется $watch, увеличивайте счетчик renderCount. Затем, в другом событии таймера, проверьте, не изменился ли счетчик renderCount за прошлый период через 3-5 секунд, тогда мы можем сделать предположение, что рендеринг сделан.

Код для наблюдения за детьми и проверка того, не происходит ли более рендеринг, может быть следующим:

app.directive('whenRenderingDone',  function($interval, $parse){
    return {
        link: function (scope, el, attrs) {
            var renderingCount = 0;
            function watchForChildren() {
                scope.$watch(function(){
                    return $(':input', el).length;
                }, function(newVal, oldVal){
                    if (newVal) {
                        renderingCount++;
                    }
                })
            }
            watchForChildren();
            //Check counter every 3 seconds, if no change since last time, this means rendering is done.
            var checkRenderingDone = $interval(function(){
                var lastCount = lastCount || -1;
                if (lastCount === renderingCount) {
                    var func = $parse(attrs.whenRenderingDone);
                    $interval.cancel(checkRenderingDone);
                    func(scope);
                }
                lastCount = renderingCount || -1;
            }, 3000);
        }
    }
});

Я попытаюсь реализовать вышеупомянутый подход, и если у вас есть обратная связь, пожалуйста, дайте мне знать.

Тарек

Ответ 1

Вы можете использовать $$postDigest для запуска кода после завершения цикла дайджеста. Вы можете больше узнать о жизненном цикле области здесь

// Some $apply action here or simply entering the digest cycle
scope.$apply(function () { ... });
...
scope.$$postDigest(function () {
  // Run any code in here that will run after all the watches complete
  // in the digest cycle. Which means it runs once after all the 
  // watches manipulate the DOM and before the browser renders
});

Ответ 2

Я разработал следующую директиву, которая хорошо работает в Chrome и IE11:

app.directive('whenRenderingDone',  function($timeout, $parse){
    return {
        link: function (scope, el, attrs) {
            var lastCount;
            var lastTimer = 5000; // Initial timeout
            //Check counter every few seconds, if no change since last time, this means rendering is done.
            var checkRenderingDone = function (){
                var mainPromiseResolved = scope.mainPromiseResolved;
                lastCount = lastCount || -1;
                if (lastCount === el.find('*').length && mainPromiseResolved) {
                    console.log('Rendering done, lastCount = %i', lastCount);
                    var func = $parse(attrs.whenRenderingDone);
                    func(scope);
                } else {
                    lastCount = el.find('*').length;
                    console.log('mainPromiseResolved = %s, lastCount %i', mainPromiseResolved, lastCount)
                    console.log('Rendering not yet done. Check again after %i seconds.', lastTimer/1000.00);
                    stopCheckRendering = $timeout(checkRenderingDone, lastTimer); 
                    lastTimer = lastTimer - 1000;
                    if (lastTimer <= 0) {
                        lastTimer = 1000;
                    }
                    return stopCheckRendering;
                }
            }
            var stopCheckRendering;
            stopCheckRendering = checkRenderingDone();
            el.on('$destroy', function() {
                if (stopCheckRendering) {
                    $timeout.cancel(stopCheckRendering);
                }
              });
        }
    }
});

Я надеюсь, что это поможет вам, и если у вас есть какие-либо комментарии для улучшения, пожалуйста, дайте мне знать. См. это, чтобы дать вам представление о том, как он работает.

Тарек