web-dev-qa-db-fra.com

Onglet Angularjs Chargement de spinner lors du rendu

J'ai une page avec quelques onglets et chaque onglet a une grande quantité de liaisons angularjs. Ceci est un exemple de page où je suis confronté à un problème.Chaque onglet prend environ 10 secondes pour être rendu. 

Donc j’avais prévu de donner un spinner de chargement pendant que l’onglet rend ... .. Donc j’avais prévu de montrer le chargement de spinner en cliquant sur l’onglet et de retirer le spinner à la fin ($last) du ng-repeat.

Dans le ng-clic sur l'onglet j'ai activé le chargeur tournant

<ul>
    <li ng-repeat="tab in tabs" 
        ng-class="{active:isActiveTab(tab.url)}" 
        ng-click="onClickTab(tab)">{{tab.title}}
   </li>
</ul>

Dans le contrôleur

$scope.onClickTab = function (tab) {
     showLoader();
     $scope.currentTab = tab.url;
 }

Pour vérifier que ng-repeat est complet, j'ai utilisé la directive ci-dessous

.directive('onFinishRender', function ($timeout) {    
    return {
        restrict: 'A',
        link: function (scope, element, attr) {
            if (scope.$last === true) {

                $timeout(function () {
                    scope.$emit('ngRepeatFinished');
                });
            }
        }
    }
});

$scope.$on('ngRepeatFinished', function (ngRepeatFinishedEvent) {
        removeLoader();
});

showLoader et removeLoader sont des fonctions simples qui ajoutent et suppriment la div ayant un simple spinner de chargement.

function showLoader() {
    $('body').append('<div class="loader"></div>');
}

function removeLoader() {
    $('.loader').fadeOut(function () {
        $(this).remove();
    });
}

Résultat attendu: le chargeur de filature doit être affiché lorsque vous cliquez sur l'onglet et apparaît jusqu'à la fin de la répétition (c.-à-d. Que l'onglet sur lequel vous avez cliqué est rendu complètement).

Résultat réel: le chargeur n'est pas affiché lorsque l'utilisateur clique dessus et il apparaît presque à la fin de ng-repaet et apparaît pendant une fraction de seconde. Ici vous pouvez observer ledit comportement. Je pense que la page n'est pas capable d'afficher le spinner à cause du processus de reliure angulaire qui rend la page gelée.

Quelqu'un peut-il m'aider à résoudre ce problème?

17
Navaneet

Vous pouvez changer votre code comme ceci:

$timeout(function() {
  $scope.currentTab = tab.url 
}, 100);

Démo: http://jsfiddle.net/eRGT8/1053/

Ce que je fais, c’est que je pousse le changement currentTab au prochain cycle de résumé. (C'est une sorte de bidouille, pas une solution dont je suis fier.) Par conséquent, lors du premier cycle de résumé (avant le $timeout invoqué), seul le chargement est indiqué. Lors du cycle de résumé suivant, le code ng-repeat bloquant commence à fonctionner mais, comme nous rendons visible le chargement précédemment, il apparaît. 

:)

Cela résout votre problème, mais une longue exécution et le blocage de JavaScript qui bloque complètement le navigateur ne constituent pas une bonne expérience utilisateur. De plus, comme le navigateur est complètement bloqué, le gif de chargement ne s'animera pas, il ne sera visible que.

13
Umut Benzer

Je recommanderais FORTEMENT la lecture de ce fil SO d’abord, écrit par l’auteur d’AngularJS lui-même, qui aborde le problème fondamental.

Retarder le changement de route AngularJS jusqu'au chargement du modèle pour éviter le scintillement

Misko demande ensuite à sa propre question concernant ce sujet en suggérant l’utilisation de $ routeProvider, comme suit:

 $routeProvider.
      when('/phones', {
        templateUrl: 'partials/phone-list.html', 
        controller: PhoneListCtrl, 
        resolve: PhoneListCtrl.resolve}). // <-- this is the connection when resolved
      when('/phones/:phoneId', {
        templateUrl: 'partials/phone-detail.html', 
        controller: PhoneDetailCtrl, 
        resolve: PhoneDetailCtrl.resolve}).
      otherwise({redirectTo: '/phones'});

Voici le produit/solution fini que vous recherchez, qui est une version améliorée de la démonstration de téléphone AngularJS:

http://mhevery.github.io/angular-phonecat/app/#/phones

Cela nécessite NgRoute (), mais c'est la bonne approche. Utiliser un $ timeout ici me semble un peu compliqué - je ne dis pas que vous avez besoin de ngRoute, mais je ne sais pas comment le retirer sans lui (et je me rends compte que vos onglets ne sont peut-être pas acheminés actuellement, mais j'espère que cela aide quand même.) .

En outre, peut également trouver angular.element (document) .ready (function () {}); pour votre charge initiale va également résoudre vos woahs.

angular.element(document).ready(function(){
  $('.loading').remove(); // Just an example dont modify the dom outside of a directive!
  alert('Loaded!');
});

Comme vous pouvez le voir, pas de $ timeout (function () {}); est utilisé dans les deux approches, voici la preuve du fonctionnement de angular.element.ready () - sans routeur: 

WORKING DEMOhttp://codepen.io/nicholasabrams/pen/MwOMNR

* Si vous avez vraiment besoin de moi, je peux créer des onglets mais vous n'avez pas donné d'exemple de code fonctionnel dans votre message. J'ai donc utilisé une autre démo que j'ai faite pour un autre thread afin de montrer la solution.

ICI IS ENCORE UNE AUTRE SOLUTION, cette fois-ci, j'ai utilisé votre exemple!

http://jsfiddle.net/eRGT8/1069/

Cette fois, je déclenche une fonction lorsque $ index === $ last (dernier élément de ngRepeat ()) de cette façon, le chargeur est supprimé du domaine ou masqué lorsque la répétition est terminée.)

6
Alpha G33k

Je pense qu'il n'est pas possible d'afficher le chargement de spinner lors du rendu de la page. Le moteur de rendu est mono-thread. Presque tout, sauf les opérations de réseau, se déroule en un seul thread. Dans Firefox et Safari, c'est le fil conducteur du navigateur. En chrome c'est le fil conducteur du processus de tabulation. Les opérations réseau peuvent être effectuées par plusieurs threads parallèles. 

Vous pouvez lire ci-dessus que ici

Il n’ya qu’une façon de résoudre ce problème - redessiner la page: ne montrer qu’une partie des données, un chargement différé, etc.

1
Mikalai

Je résous généralement cela dans mon code HTML en utilisant ng-if.

Dans votre cas, cela deviendrait quelque chose comme:

<ul>
  <span ng-if="!tabs">Content loading...</span>
  <span ng-if="tabs && tabs.length < 1">No tabs available</span>
  <span ng-if="tabs && tabs.length > 0">
    <li ng-repeat="tab in tabs" 
        ng-class="{active:isActiveTab(tab.url)}" 
        ng-click="onClickTab(tab)">{{tab.title}}
   </li>
  </span>
</ul>
1
Carsten

Vous pouvez créer une directive personnalisée pour chaque exemple de chargement de compteur, puis ajouter un service de dépendance qui contiendra un compteur et, dans chaque directive, vous pouvez appeler

var app = angular.module('app', []);

app.controller('List', function(LoadingCounter) {

  this.tabs = [{
    title: "Test tab",
    url: "http://google.sk"
  }, {
    title: "Test tab",
    url: "http://google.sk"
  }, {
    title: "Test tab",
    url: "http://google.sk"
  }]

  this.LoadingCounter = LoadingCounter;

});

app.directive('spinningLoader', function(LoadingCounter) {
  return {
    restrict: 'A',
    scope: {
      max: "="
    },
    link: function(scope) {
      LoadingCounter.updateCounter(scope.max);
    }
  }
});

app.factory('LoadingCounter', function($timeout) {
  var service = {};

  service.currentCounter = 0;
  service.finished = false;
  service.updateCounter = function(max) {
    service.currentCounter++;
    if (service.currentCounter == max) {
      //example timeout
      $timeout(function() {
        service.finished = true;
      }, 2000);

    }
  }
  service.isFinished = function() {
    return service.finished;

  }
  return service;
});
.hidden {
  display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<body ng-app="app" ng-controller="List as _">
  <div ng-if="!_.LoadingCounter.isFinished()">Loading</div>
  <ul ng-class="{'hidden' : !_.LoadingCounter.isFinished() }">
    <li spinning-loader max="_.tabs.length" ng-repeat="tab in _.tabs" ng-class="{active:isActiveTab(tab.url)}" ng-click="onClickTab(tab)">{{tab.title}}
    </li>
  </ul>
</body>

0
Milos Mosovsky