example angularjs

example - Mostrando Spinner GIF durante la solicitud de $ http en AngularJS



ng src angular 6 (25)

Estoy usando el servicio $http de angular para realizar una solicitud ajax.

¿Cómo mostrar un gif del cargador durante la solicitud de ajax?

No veo ningún evento ajaxstartevent o similar en la documentación.


Acabo de descubrir la directiva de angular-busy que muestra un pequeño cargador en función de alguna llamada asíncrona.

Por ejemplo, si tiene que hacer un GET , haga referencia a la promesa en su $scope ,

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

y llamalo asi

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

Aquí está el GitHub .

También puede instalarlo utilizando bower (no olvide actualizar las dependencias de su proyecto):

bower install angular-busy --save


Aquí está mi implementación, tan simple como un ng-show y un contador de solicitudes.

Utiliza un nuevo servicio para toda solicitud a $ 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--; }); } } ]);

Y luego puede usar su base de carga en el número de llamadas actuales:

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


Aquí está mi solución que creo que es mucho más fácil que la otra publicada aquí. No estoy seguro de cuán "bonita" es, pero resolvió todos mis problemas

Tengo un estilo css llamado "cargando"

.loading { display: none; }

El html para el div de carga puede ser lo que sea, pero usé algunos iconos de FontAwesome y el método de giro allí:

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

En los elementos que desea ocultar simplemente escriba esto:

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

y en la función acabo de configurar esto en carga.

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

Estoy usando SignalR, así que en la función hubProxy.client.allLocks (cuando se hace a través de los bloqueos) pongo

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

Esto también oculta el {{someField}} cuando la página se está cargando ya que estoy configurando la clase de carga en la carga y AngularJS la elimina después.


Compartí mi versión de la gran respuesta de @Bulltorious, actualizada para versiones angulares más nuevas (utilicé la versión 1.5.8 con este código), y también incorporé la idea de @ JMaylin de usar un contador para ser robusto a múltiples solicitudes simultáneas, y opción para omitir la visualización de la animación para solicitudes que toman menos de un número mínimo de milisegundos:

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


Dado que la funcionalidad de position: fix ha cambiado recientemente, tuve dificultades para mostrar el cargador de gifs por encima de todos los elementos, por lo que tuve que usar el jQuery incorporado de 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 */

Controlador

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

Espero que esto ayude :)


Esta es una forma sencilla de mostrar un spinner que no requiere una biblioteca de terceros, interceptadores o jQuery.

En el controlador, establecer y restablecer una bandera.

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

En el HTML, use la bandera:

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

vm.starting se establece como true cuando XHR se inicia y se borra cuando se completa XHR.


Esto funciona bien para mi:

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>

Angular:

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

Mientras que la promesa devuelta de $ http está pendiente, ng-show evaluará que sea "veraz". Esto se actualiza automáticamente una vez que se resuelve la promesa ... que es exactamente lo que queremos.


Esto realmente depende de su caso de uso específico, pero una forma simple seguiría un patrón como este:

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

Y luego reacciona a ella en tu plantilla:

<div class="spinner" ng-show="loading"></div> <div ng-repeat="item in items>{{item.name}}</div>


La siguiente forma tomará nota de todas las solicitudes y se ocultará solo una vez que se hayan completado todas las solicitudes:

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


Otra solución para mostrar la carga entre diferentes cambios de url es:

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

Y luego, en el marcado, simplemente alterne la rueda giratoria con ng-show="loading" .

Si desea mostrarlo en las solicitudes ajax, simplemente agregue $scope.loading++ cuando se inicie la solicitud y cuando finalice agregue $scope.loading-- .


Para cargas de página y modales, la forma más sencilla es usar ng-show direcive y usar una de las variables de datos de alcance. Algo como ng-show = "angular.isUndefined (scope.data.someobject)". Como los datos no están definidos, la rueda giratoria se mostrará. Una vez que el servicio vuelve con los datos y el alcance se completa, la flecha giratoria se ocultará.


Puede utilizar el interceptor angular para gestionar llamadas de solicitud 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://.com/a/49632155/4976786


Puedes probar algo como esto también:

Crear directiva:

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

Luego agregas algo como esto a mainCtrl

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

Y HTML puede verse así:

<div class="buttons loader"> <span class="icon" loader="isLoading()"></span> </div>


Si desea mostrar el cargador para cada llamada de solicitud http, entonces puede usar el interceptor angular para administrar las llamadas de solicitud http,

aquí hay un código de muestra

<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>


Si está envolviendo sus llamadas de api dentro de un servicio / fábrica, entonces puede rastrear el contador de carga allí (por answer y excelente sugerencia simultánea de @JMaylin), y consultar el contador de carga mediante una directiva. O cualquier combinación de los mismos.

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

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

USO

<div ng-controller="myCtrl" your-api-spinner> ... </div>


Si está utilizando ngResource, el atributo $ resolvido de un objeto es útil para los cargadores:

Para un recurso de la siguiente manera:

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

Puede vincular un cargador al atributo $ resolvido del objeto de recurso:

<div ng-hide="user.$resolved">Loading ...</div>


Todas las respuestas son o son complicadas, o es necesario establecer algunas variables en cada solicitud, lo cual es una práctica muy incorrecta si conocemos el concepto DRY. Aquí, un simple ejemplo de interceptor, puse el mouse en espera cuando se inicia ajax y lo configuro en automático cuando termina ajax.

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

El código se debe agregar en la aplicación app.config . Mostré cómo cambiar el mouse al cargar el estado, pero allí es posible mostrar / ocultar cualquier contenido del cargador, o agregar, eliminar algunas clases css que muestran el cargador.

Interceptor se ejecutará en cada llamada ajax, por lo que no es necesario crear variables booleanas especiales ($ scope.loading = true / false, etc.) en cada llamada http.


crear directiva con este código:

$scope.$watch($http.pendingRequests, toggleLoader); function toggleLoader(status){ if(status.length){ element.addClass(''active''); } else { element.removeClass(''active''); } }



Aquí están los encantamientos actuales de AngularJS pasados:

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'']). //.......

Aquí está el resto (HTML / CSS) .... usando

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

para cambiarlo. NOTA: lo anterior se usa en el módulo angular al comienzo del 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>


Aquí hay una versión que usa una directive y ng-hide .

Esto mostrará el cargador durante todas las llamadas a través del servicio $http angular.

En la plantilla:

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

directiva:

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

Al utilizar la clase ng-hide en el elemento, puede evitar jquery.

Personalizar: añadir un interceptor

Si crea un interceptor de carga, puede mostrar / ocultar el cargador en función de una condición.

directiva:

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

interceptador:

  • por ejemplo: no muestres spinner cuando response.background === true;
  • request intercepción y / o response para establecer $rootScope.$broadcast("loader_show"); o $rootScope.$broadcast("loader_hide");

Más información sobre cómo escribir un interceptor.


Esta es la forma más fácil de agregar un hilandero, supongo:

Puedes usar ng-show con la etiqueta div de cualquiera de estas hermosas hilanderas http://tobiasahlin.com/spinkit/ {{Esta no es mi página}}

y entonces puedes usar este tipo de lógica

//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


Se utiliza después del interceptor para mostrar la barra de carga en una solicitud 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; }]);


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


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>

Añade este 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; }

Y solo en su complemento angular:

$ rootScope.loading = false; $ rootScope.loading = true; -> cuando termina $ http.get.