openclassroom - javascript synchrone ou asynchrone




Comment créer un indicateur de chargement pour chaque action asynchrone(en utilisant $ q) dans une application angularjs (3)

Je sais qu'il existe plusieurs approches aux indicateurs de chargement en js angulaires (celui-ci, par exemple: https://gist.github.com/maikeldaloo/5140733 ).

Mais ils doivent soit être configurés pour chaque appel, soit, s'ils agissent globalement, comme je le souhaite, simplement appliquer aux requêtes http, mais pas aux promesses $ q utilisées dans les services.

Les indicateurs de chargement globaux, j'ai vu jusqu'ici, travaillent avec

$httpProvider.responseInterceptors.push(interceptor);

Y at-il quelque chose de similaire pour $q , comme $qProvider.reponseInterceptors ? Et si non, quel serait le moyen le plus pratique d'implémenter une telle fonctionnalité? Est-il possible d'utiliser un modèle de décorateur par exemple?


Bien que je trouve cela très compliqué, inutile et probablement cassé, vous pouvez décorer $q et remplacer sa fonction de defer .

Chaque fois que quelqu'un demande un nouveau defer() il exécute votre propre version qui incrémente également un compteur. Avant de distribuer l'objet de defer , vous enregistrez un callback finally (Angular 1.2.0 uniquement, mais peut always convenir aussi) pour décrémenter le compteur.

Enfin, vous ajoutez une surveillance à $rootScope pour surveiller lorsque ce compteur est supérieur à 0 (plus rapide que d'avoir pendingPromisses dans $rootScope et à lier comme ng-show="pendingPromisses > 0" ).

app.config(function($provide) {
    $provide.decorator('$q', ['$delegate', '$rootScope', function($delegate, $rootScope) {
      var pendingPromisses = 0;
      $rootScope.$watch(
        function() { return pendingPromisses > 0; }, 
        function(loading) { $rootScope.loading = loading; }
      );
      var $q = $delegate;
      var origDefer = $q.defer;
      $q.defer = function() {
        var defer = origDefer();
        pendingPromisses++;
        defer.promise.finally(function() {
          pendingPromisses--;
        });
        return defer;
      };
      return $q;
    }]);
});

Ensuite, la vue liée à une étendue qui hérite de $rootScope peut avoir:

<span ng-show="loading">Loading, please wait</span>

(cela ne fonctionnera pas dans les directives avec des étendues isolées)

Voyez-le vivre here .


Comme demandé par l'OP, ceci est basé sur la méthode que nous utilisons pour l'application sur laquelle nous travaillons actuellement. Cette méthode ne modifie PAS le comportement de $q , mais ajoute une API très simple pour gérer les promesses nécessitant une sorte d'indication visuelle. Bien que cela nécessite des modifications à tous les endroits, il ne s’agit que d’une seule page.

Usage

Il existe un service, par exemple ajaxIndicator , qui sait comment mettre à jour une partie de l'interface utilisateur. Chaque fois qu'un objet prometteur doit fournir une indication jusqu'à ce que la promesse soit résolue, nous utilisons:

// $http example:
var promise = $http.get(...);
ajaxIndicator.indicate(promise); // <--- this line needs to be added

Si vous ne voulez pas garder une référence à la promesse:

// $http example without keeping the reference:
ajaxIndicator.indicate($http.get(...));

Ou avec une ressource:

var rc = $resource(...);
...
$scope.obj = rc.get(...);
ajaxIndicator.indicate($scope.obj);

(REMARQUE: pour Angular 1.2, cela nécessiterait un tweeking, car il n’ya pas de $then() sur l’objet de ressource).

Maintenant, dans le modèle racine, vous devrez lier l'indicateur à $rootScope.ajaxActive , par exemple:

<div class="ajax-indicator" ng-show="ajaxActive"></div>

la mise en oeuvre

(Modifié depuis notre source.) ATTENTION: Cette implémentation ne prend pas en compte les appels imbriqués! (Nos exigences nécessitaient le blocage de l'interface utilisateur, nous n'attendons donc pas d'appels imbriqués; si cela vous intéresse, je pourrais essayer d'améliorer ce code.)

app.service("ajaxIndicator", ["$rootScope"], function($rootScope) {
    "use strict";

    $rootScope.ajaxActive = false;

    function indicate(promise) {
        if( !$rootScope.ajaxActive ) {
            $rootScope.ajaxActive = true;
            $rootScope.$broadcast("ajax.active"); // OPTIONAL
            if( typeof(promise) === "object" && promise !== null ) {
                if( typeof(promise.always) === "function" ) promise.always(finished);
                else if( typeof(promise.then) === "function" ) promise.then(finished,finished);
                else if( typeof(promise.$then) === "function" ) promise.$then(finished,finished);
            }
        }
    }

    function finished() {
        $rootScope.ajaxActive = false;
    }

    return {
        indicate: indicate,
        finished: finished
    };
});

J'ai eu la même grande question il y a quelques semaines et il m'arrive de faire des directives pour représenter l'état de chargement sur les boutons d'action et le chargement de contenu répétitif.

Je viens de passer un peu de temps et l'ai poussé sur github: https://github.com/ocolot/angularjs_loading_buttons

J'espère que ça aide.





angularjs