¿Mostrar GIF giratorio durante una solicitud de $ http en AngularJS?

234

Estoy usando el $http servicio de AngularJS para hacer una solicitud de Ajax.

¿Cómo se puede mostrar un GIF giratorio (u otro tipo de indicador de ocupado) mientras se ejecuta la solicitud de Ajax?

No veo nada como ajaxstarteventen la documentación de AngularJS.

Ajay Beniwal
fuente
2
Si quieres un spinner simple basado en Interceptores HTTP, tengo un módulo angular para eso. Utiliza la popular ruleta Identified Sham. Echa un vistazo: github.com/harinair/angular-sham-spinner
Hari Gangadharan
1
Escribí un plugin angular-httpshooter , lanza un evento con datos de configuración justo antes del disparo de la llamada y lanza otro justo después de recibir la respuesta, puede escribir cargadores globales capturando esos eventos
Siddharth

Respuestas:

88

Aquí están los encantamientos actuales de 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']).
//.......

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

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

para alternarlo. NOTA: lo anterior se usa en el módulo angular al comienzo de la publicación

#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>
tormentoso
fuente
19
Tenga en cuenta que puede usar en angular.element('#mydiv').show()lugar de$('#mydiv').show()
JMaylin
8
esto no funciona si su página realiza múltiples solicitudes ajax. Ocultará el gif de carga una vez que finalicen las primeras solicitudes.
Meeker
38
De acuerdo con las mejores prácticas de AngularJS (que la solución aceptada está violando), no debería modificar el DOM fuera de una directiva. Considere invocar mostrar / ocultar elementos desde una directiva.
Mariusz
77
No estoy seguro de saber lo suficiente como para comentar ... Pero, ¿no sería la forma correcta de hacerlo usando la ng-classdirectiva en lugar de usar JQuery para mostrar y ocultar elementos?
James Heald el
2
@JamesHeald es correcto ... nunca debería necesitar usar jQuery para ninguna manipulación de dom en Angular. Examina: ng-class, ng-if, ng-hide, ng-show, etc. Hay una directiva para casi todo lo que puedas necesitar.
jKlaus
471

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 él en tu plantilla:

<div class="spinner" ng-show="loading"></div>
<div ng-repeat="item in items>{{item.name}}</div>
Josh David Miller
fuente
223
Si tiene varias solicitudes ajax al mismo tiempo, puede declarar loadingcomo un número entero $scope.loading = 0;, cuando comienza una solicitud $scope.loading++;, y cuando termina, lo hace $scope.loading--;. Y no hay nada que cambiar en la plantilla. Por lo tanto, la rueda giratoria se muestra cuando hay al menos una solicitud en progreso y está oculta el resto del tiempo
JMaylin
16
Probablemente también debería establecer la carga en falso en la función de recuperación de errores.
sebnukem
3
@sebnukem Tienes razón. Este fue un ejemplo de alto nivel, pero seguí adelante y lo agregué de todos modos para mayor claridad. ¡Gracias por la respuesta!
Josh David Miller
1
@JMaylin: puede usar $ watch en $ scope.loading para hacer otras cosas
Jason
55
Una forma aún más limpia, en lugar de poner $scope.loading = falsetanto la devolución de llamada correcta como la incorrecta, es ponerla en el .finally(). Se vería así:mySvc.get().then(success, fail).finally(function() { $scope.loading = false; });
Tom
44

Aquí hay una versión usando a directivey ng-hide.

Esto mostrará el cargador durante todas las llamadas a través del $httpservicio de 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 usar la ng-hideclase en el elemento, puede evitar jquery.


Personalizar: agregar 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 mostrar spinnercuandoresponse.background === true;
  • Interceptar requesty / o responseestablecer $rootScope.$broadcast("loader_show");o$rootScope.$broadcast("loader_hide");

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

punkrockpolly
fuente
@punkrockpolly En mi escenario específico, tengo dos componentes individuales que llaman a un servicio. Pero esto solo funciona para uno de ellos. ¿Debería pasar un parámetro o algo para que sea reutilizable?
RicardoGonzales
@razorblade girará cada vez que realice una llamada a través $httpde cualquier servicio.
punkrockpolly
31

Si está utilizando ngResource, el atributo $ resuelto 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 $ resuelto del objeto de recurso:

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

Acabo de descubrir la angular-busydirectiva que muestra un pequeño cargador dependiendo de alguna llamada asíncrona.

Por ejemplo, si tiene que hacer una GETreferencia a la promesa en su $scope,

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

y llámalo así:

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

Aquí está el GitHub.

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

bower install angular-busy --save
Baltasar
fuente
No pude encontrar cómo "deshabilitar algún elemento" en la documentación. ¿Puedes explicar cómo? Por ejemplo, quiero deshabilitar un botón mientras se muestra la ruleta.
c4k
angular-busy solo pone en su elemento una máscara, no creo que pueda deshabilitar un botón como lo desea, pero puede configurar el fondo con una plantilla vacía para dar esta impresión. No soy un hablante nativo, perdón por la confusión :)
Balthazar
Ok, edité tu respuesta para evitar que alguien más tenga la misma confusión. Un francés que habla inglés a otro francés siempre es confuso :)
c4k
5

Si está envolviendo sus llamadas de API dentro de un servicio / fábrica, puede rastrear el contador de carga allí (por respuesta y excelente sugerencia simultánea de @JMaylin), y hacer referencia al contador de carga a través de una directiva. O cualquier combinación de los mismos.

Envoltorio API

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>
drzaus
fuente
5

Para cargas de página y modales, la forma más fácil es usar la ng-showdirectiva y usar una de las variables de datos de alcance. Algo como:

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

Aquí, mientras someObjectno está definido, se mostrará la rueda giratoria. Una vez que el servicio regresa con datos y someObjectse completa, el control giratorio volverá a su estado oculto.

Arnold.Krumins
fuente
3

Esta es la forma más fácil de agregar una ruleta, supongo:

Puede usar ng-show con la etiqueta div de cualquiera de estos hermosos hilanderos http://tobiasahlin.com/spinkit/ {{Esta no es mi página}}

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

ashish malgawa
fuente
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>

Agregue 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 tu complemento angular:

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

cabaji99
fuente
2

Compartí mi versión de la gran respuesta de @bulltorious, actualizada para las nuevas construcciones angulares (usé 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 el 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;
      }
    }
  }]);
qwwqwwq
fuente
2

Puede usar 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://stackoverflow.com/a/49632155/4976786

Om Sharma
fuente
2

Manera simple sin interceptores o jQuery

Esta es una manera simple de mostrar una ruleta que no requiere una biblioteca de terceros, interceptores o jQuery.

En el controlador, configure y restablezca 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>

El vm.startingindicador se establece truecuando se inicia el XHR y se borra cuando se completa el XHR.

georgeawg
fuente
1

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 la promesa devuelta por $ http está pendiente, ng-show la evaluará como "verdadera". Esto se actualiza automáticamente una vez que se resuelve la promesa ... que es exactamente lo que queremos.

húmedo
fuente
Esta no es la forma dinámica de hacer eso.
Umair Hamid
¿Podría explicar por qué cree que esta no es una forma dinámica de lograr este objetivo? Esto ayudará a otros a leer esta publicación a aprender. Gracias.
húmedo
Tengo varias solicitudes en mi solicitud. $ scope.req solo funcionará para una sola solicitud. Cuando esta solicitud en particular, el cargador completo se ocultará y no esperará a completar otras solicitudes. La mejor manera de lograr esto es usar interceptores.
Umair Hamid
1

Se utiliza después del interceptor para mostrar la barra de carga en la 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;
}]);
Dinesh Vaitage
fuente
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);
  }
}
usuario1853287
fuente
1
Las respuestas de solo código no son muy útiles.
Phantômaxx
0

crear directiva con este código:

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

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

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 alterna la rueda giratoria con ng-show="loading".

Si desea mostrarlo en solicitudes ajax, simplemente agregue $scope.loading++cuando comience la solicitud y cuando termine de agregar $scope.loading--.

Chrillewoodz
fuente
0

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 agrega 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 tener este aspecto:

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

La siguiente forma tomará nota de todas las solicitudes y se ocultará solo una vez que se hayan realizado 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;
        }
    };
})

Yerken
fuente
0

Dado que la funcionalidad de position: fixed cambió recientemente, tuve dificultades para mostrar el cargador de gif sobre todos los elementos, así que tuve que usar 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 :)

Magor Menessy
fuente
0

Todas las respuestas son complicadas, o necesitan establecer algunas variables en cada solicitud, lo cual es una práctica muy incorrecta si conocemos el concepto DRY. Aquí, simple ejemplo de interceptor, configuro el mouse en espera cuando se inicia ajax y lo configuro en automático cuando finaliza 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 debe agregarse en la configuración de 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 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.

Maciej Sikora
fuente
0

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

Utiliza un nuevo servicio para todas las solicitudes 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 cargador en la cantidad de llamadas actuales:

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

si desea mostrar el cargador para cada llamada de solicitud http, 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>
Om Sharma
fuente
0

Solo usa ng-show y un booleano

No es necesario utilizar una directiva, no es necesario complicarse.

Aquí está el código para poner al lado del botón de envío o donde quiera que esté la ruleta:

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

Y luego en tu controlador:

$scope.dataIsLoading = true

let url = '/whatever_Your_URL_Is'
$http.get(url)
.then(function(response) {
  $scope.dataIsLoading = false
})
Adán
fuente
-1

Aquí está mi solución, que creo que es mucho más fácil que la otra publicada aquí. Sin embargo, no estoy seguro de cuán "bonito" 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 utilicé algunos iconos 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 simplemente configuré esto en carga.

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

Estoy usando SignalR, así que en la función hubProxy.client.allLocks (cuando termina de pasar por los bloqueos) solo 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 carga y AngularJS la elimina después.

Stibay
fuente