2017-10-11 6 views
1

J'ai fait des recherches approfondies sur ce sujet, mais peu importe ce que je fais, je trouve qu'il est extrêmement difficile d'atteindre cet objectif.Comment pouvez-vous détecter lorsque le rendu HTML est terminé dans AngularJS

Je souhaite exécuter du code lorsque tous les éléments ont été entièrement rendus dans l'application Web AngularJS. Je pense que j'ai trouvé une solution suggérant d'utiliser des routeurs et des vues, mais je ne pouvais pas faire marcher ça sur mon cas, car il semble que cela nécessite une certaine configuration.

Lorsque vous avez ng-repeat et beaucoup d'imbriquée directives qui génère du HTML/contenu basé sur diverses conditions d'utilisation ng-if, je remarque que le rendu HTML continue même après document d'événement prêt est tiré ou afficher le contenu ont été chargés à savoir $viewContentLoaded événement déclenché.

L'idée la plus proche que j'ai est d'utiliser $watch sur la longueur des enfants de l'élément d'un directive donné. Chaque fois que le $watch est exécuté, incrémentez le compteur renderCount. Puis, dans un autre événement de minuterie, vérifiez si le compteur renderCount n'a pas changé au cours des 3-5 dernières secondes, alors nous pouvons faire l'hypothèse que le rendu est fait.

Le code à surveiller les enfants, et vérifier si plus le rendu est en cours, pourrait être la suivante:

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

Je vais essayer de mettre en œuvre l'approche ci-dessus, et si vous avez des commentaires s'il vous plaît laissez moi sais.

Tarek

Répondre

0

Vous pouvez utiliser $$postDigest pour exécuter du code après le cycle de digestion complète. Vous pouvez en savoir plus sur le cycle de vie du champ 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

Merci! Je vais l'essayer. Cependant, je me souviens que toute API AngularJS qui commence par '$$' n'est pas sûre à 100%, n'est-ce pas? – tarekahf

+0

Désolé, votre solution n'a pas fonctionné pour moi. Voir ma solution ci-dessus pour que vous puissiez avoir une meilleure idée de ce que j'essaie de faire. Merci! – tarekahf

0

J'ai développé la directive suivante qui fonctionne bien sous Chrome et 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); 
       } 
       }); 
     } 
    } 
}); 

J'espère que cela sera utile pour vous, et si vous avoir un commentaire à améliorer, s'il vous plaît faites le moi savoir. See this pour vous donner une idée de son fonctionnement.

Tarek