2017-10-11 236 views
1

我对这个主题做了大量的研究,但无论我做了什么,我都觉得实现这个目标非常困难。如何在AngularJS中完成HTML渲染的时候检测到

我想在AngularJS Web应用程序中完全呈现所有元素时执行代码。我想我找到了解决方案,建议使用路由器和视图,但我无法对我的情况做出这项工作,因为它似乎需要特定的配置。

当你有ng-repeat和大量的嵌套directives将基于使用ng-if各种条件生成HTML /内容,我注意到,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); 
     } 
    } 
}); 

我会尝试实施上述方法,如果您有反馈意见,请让我知道。

塔里克

回答

0

您可以使用$$postDigest消化周期完成之后运行的代码。你可以阅读更多有关范围的生命周期here

// 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 
}); 
+0

谢谢!我会试试看。但是,我记得任何以'$$'开头的AngularJS API都不是100%安全的,这是否正确? – tarekahf

+0

抱歉,您的解决方案对我无效。看到我上面的解决方案,以便您能更好地了解我正在尝试做什么。谢谢! – tarekahf

0

我制定了以下指令,它是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); 
       } 
       }); 
     } 
    } 
}); 

我希望这将是对你有所帮助,如果你有任何意见可以改进,请告诉我。 See this给你一个关于它是如何工作的想法。

Tarek