Affichage de Spinner GIF pendant la requête $ http dans AngularJS

J’utilise le service $http de angular pour faire une requête ajax.

Comment afficher un gif de chargeur lors de la requête ajax?

Je ne vois aucun ajaxstartevent ou similaire dans la documentation.

Voici les incantations actuelles d’ AngularJS:

 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 rest (HTML / CSS) …. en utilisant

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

pour le basculer NOTE: ce qui précède est utilisé dans le module angular en début de 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; } 

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 dans votre modèle:

 

Voici une version utilisant une directive et ng-hide .

Cela affichera le chargeur pendant tous les appels via le service $http angular.

Dans le template:

directif:

 angular.module('app') .directive('loading', ['$http', function ($http) { return { ressortingct: '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 classe ng-hide sur l’élément, vous pouvez éviter jquery.


Personnaliser: append 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 afficher spinner lorsque response.background === true;
  • Intercepter la request et / ou la response pour définir $rootScope.$broadcast("loader_show"); ou $rootScope.$broadcast("loader_hide");

plus d’infos sur l’écriture d’un intercepteur

Si vous utilisez ngResource, l’atsortingbut $ resolve d’un object 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’atsortingbut $ resolution de l’object ressource:

 
Loading ...

https://github.com/wongatech/angular-http-loader est un bon projet pour cela.

Exemple ici http://wongatech.github.io/angular-http-loader/

Le code ci-dessous montre un exemple de modèle / loader.tpl.html lorsqu’une demande est en cours.

 

Je viens de découvrir la directive angular-busy qui montre un petit chargeur en fonction de certains appels asynchrones.

Par exemple, si vous devez créer un GET , référencez la promesse dans votre $scope ,

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

et l’appeler ainsi:

 

Voici le GitHub .

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

 bower install angular-busy --save 

Si vous encapsulez vos appels d’API dans un service / usine, vous pouvez suivre le compteur de chargement (par réponse et excellente suggestion simultanée par @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 sortingggers 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 = '
'; /** * Show/Hide spinner with ajax */ function spinnerDirective($comstack, api) { return { ressortingct: 'EA', link: function (scope, element) { // listen for api sortinggger scope.hasSpinner = api.isOn; // attach spinner html var spin = NG.element(loaderTemplate); $comstack(spin)(scope); // bind+parse element.append(spin); } } } NG.module('yourModule') .directive('yourApiSpinner', ['$comstack', 'yourApi', spinnerDirective]); })(angular);

USAGE

 
...

Pour les chargements de pages et les modaux, le plus simple est d’utiliser ng-show direcive et d’utiliser l’une des variables de données de scope. Quelque chose comme ng-show = “angular.isUndefined (scope.data.someobject)”. Comme les données ne sont pas définies, le compteur affichera. Une fois le service renvoyé avec les données et l’étendue remplie, le compteur sera masqué.

C’est le moyen le plus simple d’append un spinner je suppose: –

Vous pouvez utiliser ng-show avec la balise div de l’un de ces beaux fileurs http://tobiasahlin.com/spinkit/ {{Ceci n’est pas ma page}}

et puis vous pouvez utiliser ce type 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 
 
//add this in your HTML at right place
 Based on Josh David Miller response:  

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 angular append:

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

Partager ma version de la bonne réponse de @bulltorious, mise à jour pour les nouvelles versions angulars (j’ai utilisé la version 1.5.8 avec ce code), et incorporé l’idée de @JMaylin d’utiliser un compteur pour être robuste à plusieurs requêtes simultanées, et Option permettant de ne pas afficher l’animation pour les demandes nécessitant moins d’un certain nombre 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; } } }]); 

Cela fonctionne bien pour moi:

HTML:

  

Angulaire:

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

Alors que la promesse renvoyée par $ http est en attente, ng-show l’évalue comme étant “véridique”. Ceci est automatiquement mis à jour une fois la promesse résolue … ce qui est exactement ce que nous voulons.

Intercepteur suivant utilisé pour afficher la barre de chargement sur la requête http

 'use ssortingct'; 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; }]); 
 .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 
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); } }

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

Une autre solution pour afficher le chargement entre différentes modifications d’URL est la suivante:

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

Et puis dans le balisage il suffit de basculer le spinner avec ng-show="loading" .

Si vous voulez l’afficher sur les requêtes ajax, ajoutez simplement $scope.loading++ démarrage de la requête et à la fin, ajoutez $scope.loading-- .

Vous pouvez aussi essayer quelque chose comme ça:

Créer une directive:

 myApp.directive('loader', function () { return { ressortingct: 'A', scope: {cond: '=loader'}, template: '', 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:

 

La méthode suivante prend en compte toutes les requêtes et ne se cache qu’une fois toutes les requêtes 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; } }; }) 

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

Html

 

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 🙂

Toutes les réponses sont ou sont compliquées, ou doivent définir des variables pour chaque requête, ce qui est une pratique très fausse si nous connaissons le concept DRY. Voici un exemple simple d’intercepteur, je mets la souris en attente lorsque ajax démarre et la règle sur auto quand 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 l’application config app.config . J’ai montré comment changer la souris lors du chargement, mais il est possible d’afficher ou de masquer le contenu du chargeur, ou d’append, de supprimer certaines classes CSS qui affichent le chargeur.

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

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

Il utilise un nouveau service pour toute demande à $ 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:

  

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

voici un exemple de code

  

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

  

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

C’est un moyen simple d’afficher un compteur qui ne nécessite pas de bibliothèque, d’intercepteurs ou de jQuery tiers.

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 le drapeau:

 

{{vm.data}}

vm.starting est défini sur true lorsque XHR démarre et est effacé à la fin du XHR.

Voici ma solution que je trouve beaucoup plus facile que les autres postées ici. Je ne suis pas sûr que ce soit “joli” mais il a résolu tous mes problèmes

J’ai un style css appelé “loading”

 .loading { display: none; } 

Le html pour le chargement peut être n’importe quoi mais j’ai utilisé des icons FontAwesome et la méthode spin:

 

Loading data

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

  

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

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

J’utilise SignalR donc dans la fonction hubProxy.client.allLocks (quand il est fini de passer à travers les serrures)

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

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