Afficher le GIF spinner lors d'une requête $ http dans AngularJS?

234

J'utilise le $httpservice d'AngularJS pour faire une demande Ajax.

Comment afficher un GIF spinner (ou un autre type d'indicateur occupé) pendant l'exécution de la demande Ajax?

Je ne vois rien de tel ajaxstarteventdans la documentation AngularJS.

Ajay Beniwal
la source
2
Si vous voulez un simple spinner basé sur des intercepteurs HTTP, j'ai un module angulaire pour ça. Il utilise le spinner Identified Sham populaire. Jetez un coup d'œil: github.com/harinair/angular-sham-spinner
Hari Gangadharan
1
J'ai écrit un plugin angular-httpshooter , il libère un événement avec des données de configuration juste avant le tir d'appel et en libère un autre juste après avoir reçu la réponse, vous pouvez écrire des chargeurs globaux capturant ces événements
Siddharth

Réponses:

88

Voici les cours passés AngularJS enchantements:

angular.module('SharedServices', [])
    .config(function ($httpProvider) {
        $httpProvider.responseInterceptors.push('myHttpInterceptor');
        var spinnerFunction = function (data, headersGetter) {
            // todo start the spinner here
            //alert('start spinner');
            $('#mydiv').show();
            return data;
        };
        $httpProvider.defaults.transformRequest.push(spinnerFunction);
    })
// register the interceptor as a service, intercepts ALL angular ajax http calls
    .factory('myHttpInterceptor', function ($q, $window) {
        return function (promise) {
            return promise.then(function (response) {
                // do something on success
                // todo hide the spinner
                //alert('stop spinner');
                $('#mydiv').hide();
                return response;

            }, function (response) {
                // do something on error
                // todo hide the spinner
                //alert('stop spinner');
                $('#mydiv').hide();
                return $q.reject(response);
            });
        };
    });

//regular angular initialization continued below....
angular.module('myApp', [ 'myApp.directives', 'SharedServices']).
//.......

Voici le reste (HTML / CSS) .... en utilisant

$('#mydiv').show(); 
$('#mydiv').hide(); 

pour l'activer. REMARQUE: ce qui précède est utilisé dans le module angulaire au début du post

#mydiv {  
    position:absolute;
    top:0;
    left:0;
    width:100%;
    height:100%;
    z-index:1000;
    background-color:grey;
    opacity: .8;
 }

.ajax-loader {
    position: absolute;
    left: 50%;
    top: 50%;
    margin-left: -32px; /* -1 * image width / 2 */
    margin-top: -32px;  /* -1 * image height / 2 */
    display: block;     
}

<div id="mydiv">
    <img src="lib/jQuery/images/ajax-loader.gif" class="ajax-loader"/>
</div>
bulltorious
la source
19
Notez que vous pouvez utiliser à la angular.element('#mydiv').show()place de$('#mydiv').show()
JMaylin
8
cela ne fonctionne pas si votre page fait plusieurs demandes ajax. Il masquera le gif de chargement après la fin des premières requêtes.
Meeker
38
Selon les meilleures pratiques d'AngularJS (que la solution acceptée viole), vous ne devriez pas modifier le DOM en dehors d'une directive. Envisagez d'appeler show / hide sur des éléments à partir d'une directive.
Mariusz
7
Je ne suis pas sûr d'en savoir suffisamment pour commenter ... Mais la bonne façon de procéder ne serait-elle pas d'utiliser la ng-classdirective au lieu d'utiliser JQuery pour afficher et masquer des éléments?
James Heald
2
@JamesHeald est correct .. vous ne devriez jamais vraiment avoir besoin d'utiliser jQuery pour toute manipulation dom dans Angular. Regardez dans: ng-class, ng-if, ng-hide, ng-show, etc. il y a une directive pour presque tout ce dont vous aurez besoin.
jKlaus
471

Cela dépend vraiment de votre cas d'utilisation spécifique, mais un moyen simple suivrait un modèle comme celui-ci:

.controller('MainCtrl', function ( $scope, myService ) {
  $scope.loading = true;
  myService.get().then( function ( response ) {
    $scope.items = response.data;
  }, function ( response ) {
    // TODO: handle the error somehow
  }).finally(function() {
    // called no matter success or failure
    $scope.loading = false;
  });
});

Et puis réagissez-y dans votre modèle:

<div class="spinner" ng-show="loading"></div>
<div ng-repeat="item in items>{{item.name}}</div>
Josh David Miller
la source
223
Si vous avez plusieurs demandes ajax en même temps, vous pouvez déclarer loadingun entier $scope.loading = 0;, quand une demande commence $scope.loading++;et quand elle se termine $scope.loading--;. Et il n'y a rien à changer dans le modèle. Ainsi, le spinner est affiché lorsqu'il y a au moins une demande en cours, et est caché le reste du temps
JMaylin
16
Vous devriez probablement également définir le chargement sur false dans la fonction de repli d'erreur.
sebnukem
3
@sebnukem Vous avez raison. C'était un exemple assez élevé, mais je suis allé de l'avant et je l'ai quand même ajouté pour plus de clarté. Merci pour les commentaires!
Josh David Miller,
1
@JMaylin - vous pouvez ensuite utiliser $ watch sur $ scope.loading faire d'autres choses
Jason
5
Une manière encore plus propre, au lieu de mettre $scope.loading = falseà la fois le succès et l'échec de rappel, est de le mettre dans le .finally(). Cela ressemblerait à:mySvc.get().then(success, fail).finally(function() { $scope.loading = false; });
Tom
44

Voici une version utilisant un directiveet ng-hide.

Cela montrera le chargeur pendant tous les appels via le $httpservice angulaire .

Dans le modèle:

<div class="loader" data-loading></div>

directif:

angular.module('app')
  .directive('loading', ['$http', function ($http) {
    return {
      restrict: 'A',
      link: function (scope, element, attrs) {
        scope.isLoading = function () {
          return $http.pendingRequests.length > 0;
        };
        scope.$watch(scope.isLoading, function (value) {
          if (value) {
            element.removeClass('ng-hide');
          } else {
            element.addClass('ng-hide');
          }
        });
      }
    };
}]);

en utilisant la ng-hideclasse sur l'élément, vous pouvez éviter jquery.


Personnaliser: ajoutez un interceptor

Si vous créez un intercepteur de chargement, vous pouvez afficher / masquer le chargeur en fonction d'une condition.

directif:

var loadingDirective = function ($rootScope) {
  return function ($scope, element, attrs) {
      $scope.$on("loader_show", function () {
          return element.removeClass('ng-hide');
      });
      return $scope.$on("loader_hide", function () {
          return element.addClass('ng-hide');
      });
  };
};

intercepteur:

  • par exemple: ne pas montrer spinnerquandresponse.background === true;
  • Intercepter requestet / ou responsedéfinir $rootScope.$broadcast("loader_show");ou$rootScope.$broadcast("loader_hide");

plus d'informations sur l'écriture d'un intercepteur

punkrockpolly
la source
@punkrockpolly Dans mon scénario spécifique, j'ai deux composants individuels appelant un service. Mais cela ne fonctionne que pour l'un d'eux. Dois-je passer un paramètre ou quelque chose pour le rendre réutilisable?
RicardoGonzales
@razorblade il tournera à chaque fois que vous passez un appel via $httpn'importe quel service.
punkrockpolly
31

Si vous utilisez ngResource, l'attribut $ resolu d'un objet est utile pour les chargeurs:

Pour une ressource comme suit:

var User = $resource('/user/:id', {id:'@id'});
var user = User.get({id: 1})

Vous pouvez lier un chargeur à l'attribut $ resolu de l'objet ressource:

<div ng-hide="user.$resolved">Loading ...</div>
Alexander Sweeney
la source
13

Je viens de découvrir la angular-busydirective qui montre un petit chargeur en fonction d'un appel asynchrone.

Par exemple, si vous devez faire une GETréférence à la promesse dans votre $scope,

$scope.req = $http.get('http://google.fr');

et appelez-le ainsi:

<div cg-busy="req"></div>

Voici le GitHub.

Vous pouvez également l'installer en utilisant bower(n'oubliez pas de mettre à jour les dépendances de votre projet):

bower install angular-busy --save
Balthazar
la source
Je n'ai pas trouvé comment "désactiver certains éléments" dans la documentation. Pouvez-vous expliquer comment? Par exemple, je veux désactiver un bouton pendant que le spinner est affiché.
c4k
angular-busy ne met sur votre élément qu'un masque, je ne pense pas que vous puissiez désactiver un bouton comme vous le souhaitez, mais vous pouvez définir la toile de fond dessus avec un modèle vide pour donner cette impression. Je ne suis pas natif, désolé pour la confusion :)
Balthazar
D'accord, j'ai modifié votre réponse pour empêcher quelqu'un d'autre d'avoir la même confusion. Un anglais parlant français à un autre français est toujours déroutant :)
c4k
5

Si vous enveloppez vos appels API dans un service / usine, vous pouvez y suivre le compteur de chargement (par réponse et excellente suggestion simultanée de @JMaylin), et référencer le compteur de chargement via une directive. Ou toute combinaison de ceux-ci.

API WRAPPER

yourModule
    .factory('yourApi', ['$http', function ($http) {
        var api = {}

        //#region ------------ spinner -------------

        // ajax loading counter
        api._loading = 0;

        /**
         * Toggle check
         */
        api.isOn = function () { return api._loading > 0; }

        /**
         * Based on a configuration setting to ignore the loading spinner, update the loading counter
         * (for multiple ajax calls at one time)
         */
        api.spinner = function(delta, config) {
            // if we haven't been told to ignore the spinner, change the loading counter
            // so we can show/hide the spinner

            if (NG.isUndefined(config.spin) || config.spin) api._loading += delta;

            // don't let runaway triggers break stuff...
            if (api._loading < 0) api._loading = 0;

            console.log('spinner:', api._loading, delta);
        }
        /**
         * Track an ajax load begin, if not specifically disallowed by request configuration
         */
        api.loadBegin = function(config) {
            api.spinner(1, config);
        }
        /**
         * Track an ajax load end, if not specifically disallowed by request configuration
         */
        api.loadEnd = function (config) {
            api.spinner(-1, config);
        }

        //#endregion ------------ spinner -------------

        var baseConfig = {
            method: 'post'
            // don't need to declare `spin` here
        }

        /**
         * $http wrapper to standardize all api calls
         * @param args stuff sent to request
         * @param config $http configuration, such as url, methods, etc
         */
        var callWrapper = function(args, config) {
            var p = angular.extend(baseConfig, config); // override defaults

            // fix for 'get' vs 'post' param attachment
            if (!angular.isUndefined(args)) p[p.method == 'get' ? 'params' : 'data'] = args;

            // trigger the spinner
            api.loadBegin(p);

            // make the call, and turn of the spinner on completion
            // note: may want to use `then`/`catch` instead since `finally` has delayed completion if down-chain returns more promises
            return $http(p)['finally'](function(response) {
                api.loadEnd(response.config);
                return response;
            });
        }

        api.DoSomething = function(args) {
            // yes spinner
            return callWrapper(args, { cache: true });
        }
        api.DoSomethingInBackground = function(args) {
            // no spinner
            return callWrapper(args, { cache: true, spin: false });
        }

        // expose
        return api;
    });

DIRECTIVE SPINNER

(function (NG) {
    var loaderTemplate = '<div class="ui active dimmer" data-ng-show="hasSpinner()"><div class="ui large loader"></div></div>';

    /**
     * Show/Hide spinner with ajax
     */
    function spinnerDirective($compile, api) {
        return {
            restrict: 'EA',
            link: function (scope, element) {
                // listen for api trigger
                scope.hasSpinner = api.isOn;

                // attach spinner html
                var spin = NG.element(loaderTemplate);
                $compile(spin)(scope); // bind+parse
                element.append(spin);
            }
        }
    }

    NG.module('yourModule')
        .directive('yourApiSpinner', ['$compile', 'yourApi', spinnerDirective]);
})(angular);

USAGE

<div ng-controller="myCtrl" your-api-spinner> ... </div>
drzaus
la source
5

Pour les chargements de page et les modaux, le moyen le plus simple consiste à utiliser la ng-showdirective et à utiliser l'une des variables de données d'étendue. Quelque chose comme:

ng-show="angular.isUndefined(scope.data.someObject)".

Ici, bien que ce ne someObjectsoit pas défini, le spinner s'affiche. Une fois que le service revient avec des données et someObjectest rempli, le spinner revient à son état caché.

Arnold.Krumins
la source
3

C'est le moyen le plus simple d'ajouter un spinner, je suppose: -

Vous pouvez utiliser ng-show avec la balise div de l'un de ces magnifiques filateurs http://tobiasahlin.com/spinkit/ {{Ce n'est pas ma page}}

puis vous pouvez utiliser ce genre de logique

//ajax start
    $scope.finderloader=true;
    
          $http({
    method :"POST",
    url : "your URL",
  data: { //your data
     
     }
  }).then(function mySucces(response) {
    $scope.finderloader=false;
      $scope.search=false;          
    $scope.myData =response.data.records;
  });
     
    //ajax end 
    
<div ng-show="finderloader" class=spinner></div>
//add this in your HTML at right place

ashish malgawa
la source
3
Based on Josh David Miller response:

  <body>
  <header>
  </header>
<div class="spinner" ng-show="loading">
  <div class="loader" ></div>
</div>

<div ng-view=""></div>

<footer>
</footer>

</body>

Ajoutez ce css:

    .loader {
  border: 16px solid #f3f3f3;
  border-radius: 50%;
  border-top: 16px solid #3498db;
  border-bottom : 16px solid black;
  width: 80px;
  height: 80px;
  -webkit-animation: spin 2s linear infinite;
  animation: spin 2s linear infinite;
  position: absolute;
  top: 45%;
  left: 45%;
}

@-webkit-keyframes spin {
  0% { -webkit-transform: rotate(0deg); }
  100% { -webkit-transform: rotate(360deg); }
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}


.spinner{
  width: 100%;
height: 100%;
z-index: 10000;
position: absolute;
top: 0;
left: 0;
margin: 0 auto;
text-align: center;
vertical-align: middle;
background: white;
opacity: 0.6;
}

Et juste dans votre angulaire, ajoutez:

$ rootScope.loading = false; $ rootScope.loading = true; -> lorsque $ http.get se termine.

cabaji99
la source
2

Partage de ma version de l'excellente réponse de @bulltorious, mise à jour pour les nouvelles versions angulaires (j'ai utilisé la version 1.5.8 avec ce code), et a également incorporé l'idée de @ JMaylin d'utiliser un compteur afin d'être robuste à plusieurs demandes simultanées, et la option pour ignorer l'affichage de l'animation pour les demandes prenant moins d'un certain nombre minimum de millisecondes:

var app = angular.module('myApp');
var BUSY_DELAY = 1000; // Will not show loading graphic until 1000ms have passed and we are still waiting for responses.

app.config(function ($httpProvider) {
  $httpProvider.interceptors.push('busyHttpInterceptor');
})
  .factory('busyHttpInterceptor', ['$q', '$timeout', function ($q, $timeout) {
    var counter = 0;
    return {
      request: function (config) {
        counter += 1;
        $timeout(
          function () {
            if (counter !== 0) {
              angular.element('#busy-overlay').show();
            }
          },
          BUSY_DELAY);
        return config;
      },
      response: function (response) {
        counter -= 1;
        if (counter === 0) {
          angular.element('#busy-overlay').hide();
        }
        return response;
      },
      requestError: function (rejection) {
        counter -= 1;
        if (counter === 0) {
          angular.element('#busy-overlay').hide();
        }
        return rejection;
      },
      responseError: function (rejection) {
        counter -= 1;
        if (counter === 0) {
          angular.element('#busy-overlay').hide();
        }
        return rejection;
      }
    }
  }]);
qwwqwwq
la source
2

Vous pouvez utiliser un intercepteur angulaire pour gérer les appels de requête http

  <div class="loader">
    <div id="loader"></div>
  </div>

<script>
    var app = angular.module("myApp", []);

    app.factory('httpRequestInterceptor', ['$rootScope', '$location', function ($rootScope, $location) {
        return {
            request: function ($config) {
                $('.loader').show();
                return $config;
            },
            response: function ($config) {
                $('.loader').hide();
                return $config;
            },
            responseError: function (response) {
                return response;
            }
        };
    }]);

    app.config(['$stateProvider', '$urlRouterProvider', '$httpProvider',
        function ($stateProvider, $urlRouterProvider, $httpProvider) {
            $httpProvider.interceptors.push('httpRequestInterceptor');
        }]);

</script>

https://stackoverflow.com/a/49632155/4976786

Om Sharma
la source
2

Manière simple sans intercepteurs ou jQuery

C'est un moyen simple de montrer un spinner qui ne nécessite pas de bibliothèque tierce, d'intercepteurs ou de jQuery.

Dans le contrôleur, définissez et réinitialisez un indicateur.

function starting() {
    //ADD SPINNER
    vm.starting = true;
    $http.get(url)
      .then(function onSuccess(response) {
        vm.data = response.data;
    }).catch(function onReject(errorResponse) {
        console.log(errorResponse.status);
    }).finally(function() {
        //REMOVE SPINNER
        vm.starting = false;
    });
};

Dans le HTML, utilisez l'indicateur:

<div ng-show="vm.starting">
    <img ng-src="spinnerURL" />
</div>

<div ng-hide="vm.starting">
    <p>{{vm.data}}</p>
</div>

L' vm.startingindicateur est défini au truedémarrage du XHR et effacé à la fin du XHR.

georgeawg
la source
1

Cela fonctionne bien pour moi:

HTML:

  <div id="loader" class="ng-hide" ng-show="req.$$state.pending">
    <img class="ajax-loader" 
         width="200" 
         height="200" 
         src="/images/spinner.gif" />
  </div>

Angulaire:

  $scope.req = $http.get("/admin/view/"+id).success(function(data) {          
      $scope.data = data;
  });

Alors que la promesse retournée par $ http est en attente, ng-show l'évaluera comme "véridique". Ceci est automatiquement mis à jour une fois la promesse résolue ... c'est exactement ce que nous voulons.

humide
la source
Ce n'est pas la manière dynamique de le faire.
Umair Hamid
Pourriez-vous s'il vous plaît expliquer pourquoi vous pensez que ce n'est pas un moyen dynamique d'atteindre cet objectif. Cela aidera les autres lecteurs à lire cet article. Je vous remercie.
dank
J'ai plusieurs demandes dans ma candidature. $ scope.req ne fonctionnera que pour une seule demande. Lorsque cette demande particulière, le chargeur complet se cachera et n'attendra pas la fin des autres demandes. La meilleure façon d'y parvenir est d'utiliser des intercepteurs.
Umair Hamid
1

Intercepteur suivant utilisé pour afficher la barre de chargement sur demande http

'use strict';
appServices.factory('authInterceptorService', ['$q', '$location', 'localStorage','$injector','$timeout', function ($q, $location, localStorage, $injector,$timeout) {

var authInterceptorServiceFactory = {};
var requestInitiated;

//start loading bar
var _startLoading = function () {
   console.log("error start loading");
   $injector.get("$ionicLoading").show();

}

//stop loading bar
var _stopLoading = function () {
    $injector.get("$ionicLoading").hide();
}

//request initiated
var _request = function (config) {
     requestInitiated = true;
    _startLoading();
    config.headers = config.headers || {};
    var authDataInitial = localStorage.get('authorizationData');
    if (authDataInitial && authDataInitial.length > 2) {
        var authData = JSON.parse(authDataInitial);
        if (authData) {
            config.headers.Authorization = 'Bearer ' + authData.token;
        }
    }
    return config;
}

//request responce error
var _responseError = function (rejection) {
   _stopLoading();
    if (rejection.status === 401) {
        $location.path('/login');
    }
    return $q.reject(rejection);
}

//request error
var _requestError = function (err) {
   _stopLoading();
   console.log('Request Error logging via interceptor');
   return err;
}

//request responce
var _response = function(response) {
    requestInitiated = false;

   // Show delay of 300ms so the popup will not appear for multiple http request
   $timeout(function() {

        if(requestInitiated) return;
        _stopLoading();
        console.log('Response received with interceptor');

    },300);

return response;
}



authInterceptorServiceFactory.request = _request;
authInterceptorServiceFactory.responseError = _responseError;
authInterceptorServiceFactory.requestError = _requestError;
authInterceptorServiceFactory.response = _response;

return authInterceptorServiceFactory;
}]);
Dinesh Vaitage
la source
0
.factory('authHttpResponseInterceptor', ['$q', function ($q) {
        return {
            request: function(config) {
                angular.element('#spinner').show();
                return config;
            },
            response : function(response) {
                angular.element('#spinner').fadeOut(3000);
                return response || $q.when(response);
            },
            responseError: function(reason) {
                angular.element('#spinner').fadeOut(3000);
                return $q.reject(reason);
            }
        };
    }]);



 .config(['$routeProvider', '$locationProvider', '$translateProvider', '$httpProvider',
            function ($routeProvider, $locationProvider, $translateProvider, $httpProvider) {
                $httpProvider.interceptors.push('authHttpResponseInterceptor');
    }
]);

in your Template
<div id="spinner"></div>


css   

#spinner,
#spinner:after {
  border-radius: 50%;
  width: 10em;
  height: 10em;
  background-color: #A9A9A9;
  z-index: 10000;
  position: absolute;
  left: 50%;
  bottom: 100px;
}
@-webkit-keyframes load8 {
  0% {
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
  }
  100% {
    -webkit-transform: rotate(360deg);
    transform: rotate(360deg);
  }
}
@keyframes load8 {
  0% {
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
  }
  100% {
    -webkit-transform: rotate(360deg);
    transform: rotate(360deg);
  }
}
user1853287
la source
1
Les réponses uniquement codées ne sont pas très utiles.
Phantômaxx
0

créer une directive avec ce code:

$scope.$watch($http.pendingRequests, toggleLoader);

function toggleLoader(status){
  if(status.length){
    element.addClass('active');
  } else {
    element.removeClass('active');
  }
}
Oleg Ozimok
la source
0

Une autre solution pour afficher le chargement entre les différents changements d'URL est:

$rootScope.$on('$locationChangeStart', function() {
  $scope.loading++;
});

$rootScope.$on('$locationChangeSuccess', function() {
  $timeout(function() {
    $scope.loading--;
  }, 300);
});

Et puis dans le balisage, basculez simplement le spinner avec ng-show="loading".

Si vous voulez l'afficher sur les demandes ajax, ajoutez simplement $scope.loading++quand la demande commence et quand elle se termine, ajoutez $scope.loading--.

Chrillewoodz
la source
0

Vous pouvez également essayer quelque chose comme ça:

Créer une directive:

myApp.directive('loader', function () {
    return {
        restrict: 'A',
        scope: {cond: '=loader'},
        template: '<span ng-if="isLoading()" class="soft"><span class="fa fa-refresh fa-spin"></span></span>',
        link: function (scope) {
            scope.isLoading = function() {
                var ret = scope.cond === true || (
                        scope.cond &&
                        scope.cond.$$state &&
                        angular.isDefined(scope.cond.$$state.status) &&
                        scope.cond.$$state.status === 0
                    );
                return ret;
            }
        }
    };
}); 

Ensuite, vous ajoutez quelque chose comme ça à mainCtrl

    // Return TRUE if some request is LOADING, else return FALSE
    $scope.isLoading = function() {
        return $http.pendingRequests.length > 0;
    };

Et HTML peut ressembler à ceci:

<div class="buttons loader">
    <span class="icon" loader="isLoading()"></span>
</div>
Andurit
la source
0

La manière suivante prendra note de toutes les demandes et ne se masquera qu'une fois toutes les demandes terminées:

app.factory('httpRequestInterceptor', function(LoadingService, requestCount) {
    return {
        request: function(config) {
            if (!config.headers.disableLoading) {
                requestCount.increase();
                LoadingService.show();
            }
            return config;
        }
    };
}).factory('httpResponseInterceptor', function(LoadingService, $timeout, error, $q, requestCount) {
    function waitAndHide() {
        $timeout(function() {
            if (requestCount.get() === 0){
                LoadingService.hide();
            }
            else{
                waitAndHide();
            }
        }, 300);
    }

    return {
        response: function(config) {
            requestCount.descrease();
            if (requestCount.get() === 0) {
                waitAndHide();
            }
            return config;
        },
        responseError: function(config) {
            requestCount.descrease();
            if (requestCount.get() === 0) {
                waitAndHide();
            }
            var deferred = $q.defer();
            error.show(config.data, function() {
                deferred.reject(config);
            });
            return deferred.promise;
        }
    };
}).factory('requestCount', function() {
    var count = 0;
    return {
        increase: function() {
            count++;
        },
        descrease: function() {
            if (count === 0) return;
            count--;
        },
        get: function() {
            return count;
        }
    };
})

Yerken
la source
0

Depuis que la fonctionnalité de position: fixed a changé récemment, j'ai eu du mal à montrer le chargeur gif au-dessus de tous les éléments, j'ai donc dû utiliser jQuery intégré d'angular .

Html

<div ng-controller="FetchController">
      <div id="spinner"></div>
</div>

Css

#spinner {display: none}
body.spinnerOn #spinner { /* body tag not necessary actually */
   display: block;
   height: 100%;
   width: 100%;
   background: rgba(207, 13, 48, 0.72) url(img/loader.gif) center center no-repeat;
   position: fixed;
   top: 0;
   left: 0;
   z-index: 9999;
}
body.spinnerOn main.content { position: static;} /* and whatever content needs to be moved below your fixed loader div */

Manette

app.controller('FetchController', ['$scope', '$http', '$templateCache', '$location', '$q',
function($scope, $http, $templateCache, $location, $q) {

angular.element('body').addClass('spinnerOn'); // add Class to body to show spinner

$http.post( // or .get(
    // your data here
})
.then(function (response) {
    console.info('success');     
    angular.element('body').removeClass('spinnerOn'); // hide spinner

    return response.data;               
}, function (response) {                   
    console.info('error'); 
    angular.element('body').removeClass('spinnerOn'); // hide spinner
});

})

J'espère que cela t'aides :)

Magor Menessy
la source
0

Toutes les réponses sont ou trop compliquées, ou doivent définir des variables à chaque demande, ce qui est une très mauvaise pratique si nous connaissons le concept DRY. Voici un exemple d'intercepteur simple, je mets la souris en attente lorsque ajax démarre et la règle sur auto lorsque ajax se termine.

$httpProvider.interceptors.push(function($document) {
    return {
     'request': function(config) {
         // here ajax start
         // here we can for example add some class or show somethin
         $document.find("body").css("cursor","wait");

         return config;
      },

      'response': function(response) {
         // here ajax ends
         //here we should remove classes added on request start

         $document.find("body").css("cursor","auto");

         return response;
      }
    };
  });

Le code doit être ajouté dans la configuration de l'application app.config. J'ai montré comment changer la souris sur l'état de chargement, mais là-dedans, il est possible d'afficher / masquer tout contenu du chargeur, ou d'ajouter, supprimer certaines classes CSS qui montrent le chargeur.

Interceptor s'exécutera à chaque appel ajax, donc pas besoin de créer de variables booléennes spéciales ($ scope.loading = true / false etc.) à chaque appel http.

Maciej Sikora
la source
0

Voici mon implémentation, aussi simple qu'un ng-show et un compteur de requêtes.

Il utilise un nouveau service pour toute demande de $ http:

myApp.service('RqstSrv', [ '$http', '$rootScope', function($http, $rootScope) {
    var rqstService = {};

    rqstService.call = function(conf) {

        $rootScope.currentCalls = !isNaN($rootScope.currentCalls) ?  $rootScope.currentCalls++ : 0;

        $http(conf).then(function APICallSucceed(response) {
            // Handle success
        }, function APICallError(response) {
            // Handle error
        }).then(function() {
            $rootScope.currentCalls--;
        });
    }
} ]);

Et puis vous pouvez utiliser votre base de chargeur sur le nombre d'appels en cours:

<img data-ng-show="currentCalls > 0" src="images/ajax-loader.gif"/>
Bancarel Valentin
la source
0

si vous souhaitez afficher le chargeur pour chaque appel de requête http, vous pouvez utiliser un intercepteur angulaire pour gérer les appels de requête http,

voici un exemple de code

<body data-ng-app="myApp">
<div class="loader">
    <div id="loader"></div>
</div>

<script>
    var app = angular.module("myApp", []);

    app.factory('httpRequestInterceptor', ['$rootScope', '$location', function ($rootScope, $location) {
        return {
            request: function ($config) {
                $('.loader').show();
                return $config;
            },
            response: function ($config) {
                $('.loader').hide();
                return $config;
            },
            responseError: function (response) {
                return response;
            }
        };
    }]);

    app.config(['$stateProvider', '$urlRouterProvider', '$httpProvider',
        function ($stateProvider, $urlRouterProvider, $httpProvider) {
            $httpProvider.interceptors.push('httpRequestInterceptor');
        }]);

</script>
</body>
Om Sharma
la source
0

Utilisez simplement ng-show et un booléen

Pas besoin d'utiliser une directive, pas besoin de se compliquer.

Voici le code à mettre à côté du bouton soumettre ou où vous voulez que le spinner soit:

<span ng-show="dataIsLoading">
  <img src="http://www.nasa.gov/multimedia/videogallery/ajax-loader.gif" style="height:20px;"/>
</span>

Et puis dans votre contrôleur:

$scope.dataIsLoading = true

let url = '/whatever_Your_URL_Is'
$http.get(url)
.then(function(response) {
  $scope.dataIsLoading = false
})
Adam
la source
-1

Voici ma solution qui, je pense, est beaucoup plus facile que l'autre publiée ici. Je ne sais pas à quel point c'est "joli", mais cela a résolu tous mes problèmes

J'ai un style CSS appelé "chargement"

.loading { display: none; }

Le code HTML pour la div de chargement peut être n'importe quoi, mais j'ai utilisé quelques icônes FontAwesome et la méthode de rotation là-bas:

<div style="text-align:center" ng-class="{ 'loading': !loading }">
    <br />
    <h1><i class="fa fa-refresh fa-spin"></i> Loading data</h1>
</div>

Sur les éléments que vous souhaitez cacher, écrivez simplement ceci:

<something ng-class="{ 'loading': loading }" class="loading"></something>

et dans la fonction je viens de mettre cela en charge.

(function (angular) {
    function MainController($scope) {
        $scope.loading = true

J'utilise SignalR donc dans la fonction hubProxy.client.allLocks (quand c'est fait en passant par les verrous)

 $scope.loading = false
 $scope.$apply();

Cela masque également le {{someField}} lorsque la page se charge, car je mets la classe de chargement en charge et AngularJS la supprime ensuite.

stibay
la source