Angularjs cargando la pantalla en la solicitud de ajax

Usando Angularjs, necesito mostrar una pantalla de carga (un spinner simple) hasta que se complete la solicitud de ajax. Por favor sugiera cualquier idea con un fragmento de código.

En lugar de configurar una variable de ámbito para indicar el estado de carga de datos, es mejor tener una directiva que haga todo por usted:

 angular.module('directive.loading', []) .directive('loading', ['$http' ,function ($http) { return { restrict: 'A', link: function (scope, elm, attrs) { scope.isLoading = function () { return $http.pendingRequests.length > 0; }; scope.$watch(scope.isLoading, function (v) { if(v){ elm.show(); }else{ elm.hide(); } }); } }; }]); 

Con esta directiva, todo lo que necesita hacer es darle a cualquier elemento de animación de carga un atributo de ‘carga’:

 

Puede tener múltiples spinners de carga en la página. dónde y cómo distribuir esos hilanderos depende de usted y la directiva simplemente lo encenderá / apagará automáticamente.

Aquí hay un ejemplo. Utiliza el método simple ng-show con un bool.

HTML

 
LOADINGhttps://stackoverflow.com/questions/17144180/angularjs-loading-screen-on-ajax-request/...
  • {{car.name}}
  • ANGULARJS

      $scope.clickMe = function() { $scope.loading = true; $http.get('test.json') .success(function(data) { $scope.cars = data[0].cars; $scope.loading = false; }); } 

    Por supuesto, puede mover el código html de la caja de carga a una directiva y luego usar $ watch en $ scope.loading. En ese caso:

    HTML :

      

    DIRECTIVA DE ANGULARJS :

      .directive('loading', function () { return { restrict: 'E', replace:true, template: '
    LOADINGhttps://stackoverflow.com/questions/17144180/angularjs-loading-screen-on-ajax-request/...
    ', link: function (scope, element, attr) { scope.$watch('loading', function (val) { if (val) $(element).show(); else $(element).hide(); }); } } })

    PLUNK : http://plnkr.co/edit/AI1z21?p=preview

    Yo uso ngProgress para esto.

    Agregue ‘ngProgress’ a sus dependencias una vez que haya incluido los archivos de script / css en su HTML. Una vez que lo haga, puede configurar algo como esto, que se activará cuando se detecte un cambio de ruta.

     angular.module('app').run(function($rootScope, ngProgress) { $rootScope.$on('$routeChangeStart', function(ev,data) { ngProgress.start(); }); $rootScope.$on('$routeChangeSuccess', function(ev,data) { ngProgress.complete(); }); }); 

    Para las solicitudes AJAX puede hacer algo como esto:

     $scope.getLatest = function () { ngProgress.start(); $http.get('/latest-goodies') .success(function(data,status) { $scope.latest = data; ngProgress.complete(); }) .error(function(data,status) { ngProgress.complete(); }); }; 

    Simplemente recuerde agregar ‘ngProgress’ a las dependencias de los controladores antes de hacerlo. Y si está haciendo múltiples solicitudes AJAX use una variable incremental en el scope de la aplicación principal para realizar un seguimiento cuando sus solicitudes AJAX hayan finalizado antes de llamar a ‘ngProgress.complete ();’.

    el uso de pendingRequests no es correcto porque, como se menciona en la documentación de Angular, esta propiedad está destinada principalmente para fines de depuración.

    Lo que recomiendo es usar un interceptor para saber si hay alguna llamada Async activa.

     module.config(['$httpProvider', function ($httpProvider) { $httpProvider.interceptors.push(function ($q, $rootScope) { if ($rootScope.activeCalls == undefined) { $rootScope.activeCalls = 0; } return { request: function (config) { $rootScope.activeCalls += 1; return config; }, requestError: function (rejection) { $rootScope.activeCalls -= 1; return rejection; }, response: function (response) { $rootScope.activeCalls -= 1; return response; }, responseError: function (rejection) { $rootScope.activeCalls -= 1; return rejection; } }; }); }]); 

    y luego verificar si activeCalls es cero o no en la directiva a través de $ watch.

     module.directive('loadingSpinner', function ($http) { return { restrict: 'A', replace: true, template: '
    ', link: function (scope, element, attrs) { scope.$watch('activeCalls', function (newVal, oldVal) { if (newVal == 0) { $(element).hide(); } else { $(element).show(); } }); } }; });

    La mejor forma de hacerlo es usar interceptores de respuesta junto con directivas personalizadas. Y el proceso puede mejorarse aún más utilizando el mecanismo pub / sub utilizando los métodos $ rootScope. $ Broadcast y $ rootScope. $ On .

    Como todo el proceso está documentado en un artículo de blog bien escrito, no voy a repetirlo aquí otra vez. Consulte ese artículo para obtener su implementación necesaria.

    En referencia a esta respuesta

    https://stackoverflow.com/a/17144634/4146239

    Para mí es la mejor solución, pero hay una forma de evitar el uso de jQuery.

     .directive('loading', function () { return { restrict: 'E', replace:true, template: '
    LOADINGhttps://stackoverflow.com/questions/17144180/angularjs-loading-screen-on-ajax-request/...
    ', link: function (scope, element, attr) { scope.$watch('loading', function (val) { if (val) scope.loadingStatus = 'true'; else scope.loadingStatus = 'false'; }); } } }) .controller('myController', function($scope, $http) { $scope.cars = []; $scope.clickMe = function() { scope.loadingStatus = 'true' $http.get('test.json') .success(function(data) { $scope.cars = data[0].cars; $scope.loadingStatus = 'false'; }); } });
       
  • {{car.name}}
  • Aplicación mecanografiada y angular

    directiva

     ((): void=> { "use strict"; angular.module("app").directive("busyindicator", busyIndicator); function busyIndicator($http:ng.IHttpService): ng.IDirective { var directive = { restrict: "A", link(scope: Scope.IBusyIndicatorScope) { scope.anyRequestInProgress = () => ($http.pendingRequests.length > 0); scope.$watch(scope.anyRequestInProgress, x => { if (x) { scope.canShow = true; } else { scope.canShow = false; } }); } }; return directive; } })(); 

    Alcance

      module App.Scope { export interface IBusyIndicatorScope extends angular.IScope { anyRequestInProgress: any; canShow: boolean; } } 

    Modelo

     
    CSS #activityspinner { display : none; } #activityspinner.show { display : block; position : fixed; z-index: 100; background-image : url('data:image/gif;base64,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') -ms-opacity : 0.4; opacity : 0.4; background-repeat : no-repeat; background-position : center; left : 0; bottom : 0; right : 0; top : 0; }

    También hay una bonita demostración que muestra cómo puedes usar la animación de Angularjs en tu proyecto.
    el enlace está aquí
    http://yearofmoo-articles.github.io/angularjs-animation-article/app/#/ng-repeat (Ver la esquina superior izquierda) .
    Es una fuente abierta. aquí está el enlace para descargar
    https://github.com/yearofmoo-articles/AngularJS-Animation-Article
    Y aquí está el enlace para el tutorial;
    http://www.yearofmoo.com/2013/04/animation-in-angularjs.html
    Mi punto es, adelante y descargue los archivos fuente y luego vea cómo han implementado el spinner. Podrían haber usado un enfoque un poco mejor. Por lo tanto, revisa este proyecto.

    Si está utilizando Restangular (que es increíble) puede crear una animación durante las llamadas de API. Aquí está mi solución. Agregue un interceptor de respuesta y un interceptor de solicitud que envíe una transmisión de rootscope. Luego crea una directiva para escuchar esa respuesta y solicitud .:

      angular.module('mean.system') .factory('myRestangular',['Restangular','$rootScope', function(Restangular,$rootScope) { return Restangular.withConfig(function(RestangularConfigurer) { RestangularConfigurer.setBaseUrl('http://localhost:3000/api'); RestangularConfigurer.addResponseInterceptor(function(data, operation, what, url, response, deferred) { var extractedData; // .. to look for getList operations if (operation === 'getList') { // .. and handle the data and meta data extractedData = data.data; extractedData.meta = data.meta; } else { extractedData = data.data; } $rootScope.$broadcast('apiResponse'); return extractedData; }); RestangularConfigurer.setRequestInterceptor(function (elem, operation) { if (operation === 'remove') { return null; } return (elem && angular.isObject(elem.data)) ? elem : {data: elem}; }); RestangularConfigurer.setRestangularFields({ id: '_id' }); RestangularConfigurer.addRequestInterceptor(function(element, operation, what, url) { $rootScope.$broadcast('apiRequest'); return element; }); }); }]); 

    Aquí está la directiva:

      angular.module('mean.system') .directive('smartLoadingIndicator', function($rootScope) { return { restrict: 'AE', template: '

     Loading

    ', replace: true, link: function(scope, elem, attrs) { scope.isAPICalling = false; $rootScope.$on('apiRequest', function() { scope.isAPICalling = true; }); $rootScope.$on('apiResponse', function() { scope.isAPICalling = false; }); } }; }) ;

    Incluye esto en tu “app.config”:

      $httpProvider.interceptors.push('myHttpInterceptor'); 

    Y agrega este código:

     app.factory('myHttpInterceptor', function ($q, $window,$rootScope) { $rootScope.ActiveAjaxConectionsWithouthNotifications = 0; var checker = function(parameters,status){ //YOU CAN USE parameters.url TO IGNORE SOME URL if(status == "request"){ $rootScope.ActiveAjaxConectionsWithouthNotifications+=1; $('#loading_view').show(); } if(status == "response"){ $rootScope.ActiveAjaxConectionsWithouthNotifications-=1; } if($rootScope.ActiveAjaxConectionsWithouthNotifications<=0){ $rootScope.ActiveAjaxConectionsWithouthNotifications=0; $('#loading_view').hide(); } }; return { 'request': function(config) { checker(config,"request"); return config; }, 'requestError': function(rejection) { checker(rejection.config,"request"); return $q.reject(rejection); }, 'response': function(response) { checker(response.config,"response"); return response; }, 'responseError': function(rejection) { checker(rejection.config,"response"); return $q.reject(rejection); } }; }); 

    Usa angular-busy :

    Agregue cgBusy a su aplicación / módulo:

     angular.module('your_app', ['cgBusy']); 

    Añada su promesa al scope :

     function MyCtrl($http, User) { //using $http this.isBusy = $http.get('https://stackoverflow.com/questions/17144180/angularjs-loading-screen-on-ajax-request/...'); //if you have a User class based on $resource this.isBusy = User.$save(); } 

    En tu plantilla html:

     

    Aquí ejemplo de interceptor simple, puse el mouse en espera cuando ajax se inicia y lo configuro en automático cuando ajax termina.

     $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 debe ser agregado en la aplicación config app.config . Mostré cómo cambiar el mouse en el estado de carga, pero allí es posible mostrar / ocultar cualquier contenido del cargador, o agregar, eliminar algunas clases de 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.

    Interceptor está utilizando construido en angular jqLite https://docs.angularjs.org/api/ng/function/angular.element por lo que no es necesario Jquery.

    Crea una directiva con los atributos mostrar y tamaño (también puedes agregar más)

      app.directive('loader',function(){ return { restrict:'EA', scope:{ show : '@', size : '@' }, template : '
    ' } })

    y en html uso como

       

    En la variable show , pasa verdadero cuando se está ejecutando cualquier promesa y la convierte en falsa cuando se completa la solicitud. Demostración activa: ejemplo de directiva de Angular Loader demo en JsFiddle

    Creé un poco la respuesta de @DavidLin para simplificarlo, eliminando cualquier dependencia de jQuery en la directiva. Puedo confirmar que esto funciona como lo uso en una aplicación de producción

     function AjaxLoadingOverlay($http) { return { restrict: 'A', link: function ($scope, $element, $attributes) { $scope.loadingOverlay = false; $scope.isLoading = function () { return $http.pendingRequests.length > 0; }; $scope.$watch($scope.isLoading, function (isLoading) { $scope.loadingOverlay = isLoading; }); } }; } 

    Uso un ng-show lugar de una llamada jQuery para ocultar / mostrar el

    .

    Aquí está el

    que coloqué justo debajo de la etiqueta apertura:

     

    Y aquí está el CSS que proporciona la superposición para bloquear la interfaz de usuario mientras se realiza una llamada de $ http:

     .loading-overlay { position: fixed; z-index: 999; height: 2em; width: 2em; overflow: show; margin: auto; top: 0; left: 0; bottom: 0; right: 0; } .loading-overlay:before { content: ''; display: block; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.3); } /* :not(:required) hides these rules from IE9 and below */ .loading-overlay:not(:required) { font: 0/0 a; color: transparent; text-shadow: none; background-color: transparent; border: 0; } 

    El crédito CSS va a @Steve Seeger’s – su publicación: https://stackoverflow.com/a/35470281/335545

    Puede agregar una condición y luego cambiarla a través del ramoscopio. Antes de su solicitud de ajax, simplemente llame a $ rootScope. $ Emit (‘stopLoader’);

     angular.module('directive.loading', []) .directive('loading', ['$http', '$rootScope',function ($http, $rootScope) { return { restrict: 'A', link: function (scope, elm, attrs) { scope.isNoLoadingForced = false; scope.isLoading = function () { return $http.pendingRequests.length > 0 && scope.isNoLoadingForced; }; $rootScope.$on('stopLoader', function(){ scope.isNoLoadingForced = true; }) scope.$watch(scope.isLoading, function (v) { if(v){ elm.show(); }else{ elm.hide(); } }); } }; }]); 

    Definitivamente esta no es la mejor solución, pero aún así funcionaría.