Mostra GIF spinner durante una richiesta $ http in AngularJS?


234

Sto usando il $httpservizio di AngularJS per fare una richiesta Ajax.

Come si può visualizzare una GIF spinner (o un altro tipo di indicatore di occupato) mentre la richiesta Ajax è in esecuzione?

Non vedo nulla di simile a un ajaxstarteventdocumento nella documentazione di AngularJS.


2
Se vuoi un semplice spinner basato su intercettori HTTP, ho un modulo angolare per quello. Utilizza il famoso filatore Identified Sham. Dai un'occhiata: github.com/harinair/angular-sham-spinner
Hari Gangadharan

1
Ho scritto un plug -in angular-httpshooter , rilascia un evento con i dati di configurazione appena prima dello scatto della chiamata e ne rilascia un altro subito dopo aver ricevuto resposne, puoi scrivere caricatori globali che catturano quegli eventi
Siddharth

Risposte:


88

Ecco gli attuali incantesimi AngularJS passati:

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

Ecco il resto (HTML / CSS) .... usando

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

per attivarlo. NOTA: quanto sopra viene utilizzato nel modulo angolare all'inizio della posta

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

19
Nota che puoi usare angular.element('#mydiv').show()invece di$('#mydiv').show()
JMaylin il

8
questo non funziona se la tua pagina effettua più richieste Ajax. Nasconde la gif di caricamento al termine della prima richiesta.
Meeker,

38
Secondo le migliori pratiche di AngularJS (che la soluzione accettata sta violando) non dovresti modificare il DOM al di fuori di una direttiva. Considera di invocare show / hide su elementi all'interno di una direttiva.
Mariusz,

7
Non sono sicuro di sapere abbastanza per commentare ... Ma il modo giusto per farlo è usare la ng-classdirettiva invece di usare JQuery per mostrare e nascondere gli elementi?
James Heald,

2
@JamesHeald è corretto .. non dovresti mai aver bisogno di usare jQuery per qualsiasi manipolazione di dom in Angular. Guarda in: ng-class, ng-if, ng-hide, ng-show, ecc. C'è una direttiva per quasi tutto ciò di cui avrai mai bisogno.
jKlaus,

471

Questo dipende davvero dal tuo caso d'uso specifico, ma un modo semplice seguirebbe un modello come questo:

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

E poi reagisci ad esso nel tuo modello:

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

223
Se hai più richieste Ajax contemporaneamente, puoi dichiararle loadingcome numero intero $scope.loading = 0;, quando una richiesta inizia a farlo $scope.loading++;e quando finisce lo fai $scope.loading--;. E non c'è nulla da cambiare nel modello. Quindi lo spinner viene visualizzato quando è in corso almeno una richiesta ed è nascosto per il resto del tempo
JMaylin

16
Probabilmente dovresti impostare il caricamento su false anche nella funzione di fallback dell'errore.
sebnukem,

3
@sebnukem Hai ragione. Questo è stato un esempio piuttosto di alto livello, ma sono andato avanti e l'ho aggiunto comunque per chiarezza. Grazie per il feedback!
Josh David Miller,

1
@JMaylin - puoi quindi usare $ watch su $ scope.loading fare altre cose
Jason

5
Un modo ancora più pulito, invece di inserire $scope.loading = falsesia il successo che il fallito callback, è metterlo nel .finally(). Sembrerebbe:mySvc.get().then(success, fail).finally(function() { $scope.loading = false; });
Tom l'

44

Ecco una versione che utilizza un directivee ng-hide.

Questo mostrerà il caricatore durante tutte le chiamate tramite il $httpservizio angolare .

Nel modello:

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

direttiva:

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

usando la ng-hideclasse sull'elemento, puoi evitare jquery.


Personalizza: aggiungi un interceptor

Se si crea un intercettore di caricamento, è possibile mostrare / nascondere il caricatore in base a una condizione.

direttiva:

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

intercettore:

  • per esempio: non mostrare spinnerquandoresponse.background === true;
  • Intercetta requeste / o responseper impostare $rootScope.$broadcast("loader_show");o$rootScope.$broadcast("loader_hide");

maggiori informazioni sulla scrittura di un intercettore


@punkrockpolly Nel mio scenario specifico, ho due componenti individuali che chiamano un servizio. Ma questo funziona solo per uno di loro, devo passare un parametro o qualcosa per renderlo riutilizzabile?
RicardoGonzales,

@razorblade girerà ogni volta che effettui una chiamata $httpin qualsiasi servizio.
punkrockpolly,

31

Se stai usando ngResource, l'attributo $ resolved di un oggetto è utile per i caricatori:

Per una risorsa come segue:

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

È possibile collegare un caricatore all'attributo $ risolto dell'oggetto risorsa:

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


13

Ho appena scoperto la angular-busydirettiva che mostra un piccolo caricatore a seconda di una chiamata asincrona.

Ad esempio, se devi fare un GET, fai riferimento alla promessa nel tuo $scope,

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

e chiamalo così:

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

Ecco il GitHub.

Puoi anche installarlo usando bower(non dimenticare di aggiornare le dipendenze del tuo progetto):

bower install angular-busy --save

Non sono riuscito a trovare come "disabilitare alcuni elementi" nella documentazione. Puoi spiegare come? Ad esempio, voglio disabilitare un pulsante mentre viene mostrato lo spinner.
c4k,

angular-busy mette solo sul tuo elemento una maschera, non penso che tu possa disabilitare un pulsante come vuoi, ma puoi impostare lo sfondo su di esso con un modello vuoto per dare questa impressione. Non sono madrelingua, scusate la confusione :)
Balthazar,

Ok, ho modificato la tua risposta per evitare che qualcun altro abbia la stessa confusione. Un francese che parla inglese con un altro francese è sempre
fonte di

5

Se stai avvolgendo le tue chiamate API all'interno di un servizio / fabbrica, puoi tracciare lì il contatore di caricamento (per risposta e eccellente suggerimento simultaneo di @JMaylin) e fare riferimento al contatore di caricamento tramite una direttiva. O una loro combinazione.

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

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

5

Per caricamenti di pagine e modali, il modo più semplice è utilizzare la ng-showdirettiva e utilizzare una delle variabili dei dati dell'ambito. Qualcosa di simile a:

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

Qui, mentre someObjectè indefinito, lo spinner mostrerà. Una volta che il servizio ritorna con i dati e someObjectviene popolato, lo spinner tornerà al suo stato nascosto.


3

Questo è il modo più semplice per aggiungere un filatore immagino: -

Puoi usare ng-show con il tag div di uno di questi fantastici spinner http://tobiasahlin.com/spinkit/ {{Questa non è la mia pagina}}

e quindi puoi usare questo tipo di logica

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


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>

Aggiungi questo 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;
}

E proprio nella tua angolare aggiungi:

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


2

Condividendo la mia versione della grande risposta di @bulltorious, aggiornata per le nuove build angolari (ho usato la versione 1.5.8 con questo codice) e ho anche incorporato l'idea di @ JMaylin di utilizzare un contatore in modo da essere robusto per più richieste simultanee, e il opzione per saltare la visualizzazione dell'animazione per le richieste che richiedono meno di un numero minimo di millisecondi:

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

2

È possibile utilizzare l'interceptor angolare per gestire le chiamate di richiesta 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


2

Modo semplice senza intercettori o jQuery

Questo è un modo semplice per mostrare uno spinner che non richiede una libreria di terze parti, intercettori o jQuery.

Nel controller, impostare e ripristinare un flag.

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

In HTML, usa il flag:

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

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

Il vm.startingflag viene impostato trueall'avvio dell'XHR e cancellato al termine dell'XHR.


1

Questo funziona bene per me:

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>

Angolare:

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

Mentre la promessa restituita da $ http è in sospeso, ng-show lo valuterà come "veritiero". Questo viene automaticamente aggiornato una volta risolta la promessa ... che è esattamente quello che vogliamo.


Questo non è il modo dinamico per farlo.
Umair Hamid,

Potresti spiegare perché credi che questo non sia un modo dinamico per raggiungere questo obiettivo. Questo aiuterà gli altri a leggere questo post. Grazie.
Dank

Ho una richiesta multipla nella mia domanda. $ scope.req funzionerà solo per una singola richiesta. Quando questa particolare richiesta, il caricatore completo verrà nascosto e non attenderà il completamento di altre richieste. Il modo migliore per raggiungere questo obiettivo è utilizzare gli intercettori.
Umair Hamid,

1

Utilizzato il seguente intercettore per mostrare la barra di caricamento su richiesta 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;
}]);

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

1
Le risposte di solo codice non sono molto utili.
Phantômaxx,

0

crea direttiva con questo codice:

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

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

0

Un'altra soluzione per mostrare il caricamento tra diverse modifiche all'URL è:

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

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

E poi nel markup basta attivare / disattivare lo spinner ng-show="loading".

Se vuoi visualizzarlo su richieste Ajax basta aggiungere $scope.loading++quando inizia la richiesta e quando finisce aggiungi $scope.loading--.


0

Puoi provare anche qualcosa del genere:

Crea direttiva:

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

Quindi aggiungi qualcosa di simile a mainCtrl

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

E HTML può apparire così:

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

0

Il seguente modo prenderà nota di tutte le richieste e si nasconderà solo una volta completate tutte le richieste:

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


0

Dato che la funzionalità della posizione: riparata di recente, ho avuto difficoltà a mostrare il caricatore gif sopra tutti gli elementi, quindi ho dovuto usare jQuery incorporato di 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 */

controllore

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

})

Spero che questo ti aiuti :)


0

Tutte le risposte sono complicate o devono impostare alcune variabili su ogni richiesta, il che è una pratica molto sbagliata se conosciamo il concetto di DRY. Qui semplice esempio di intercettore, ho impostato il mouse in attesa all'avvio di Ajax e impostato su Auto al termine di 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;
      }
    };
  });

Il codice deve essere aggiunto nella configurazione dell'applicazione app.config. Ho mostrato come cambiare il mouse sullo stato di caricamento, ma all'interno è possibile mostrare / nascondere qualsiasi contenuto del caricatore o aggiungere, rimuovere alcune classi CSS che mostrano il caricatore.

Interceptor verrà eseguito su ogni chiamata ajax, quindi non è necessario creare variabili booleane speciali ($ scope.loading = true / false ecc.) Su ogni chiamata http.


0

Ecco la mia implementazione, semplice come un ng-show e un contatore di richieste.

Utilizza un nuovo servizio per tutte le richieste 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--;
        });
    }
} ]);

E quindi puoi utilizzare la tua base caricatore sul numero di chiamate correnti:

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

0

se desideri mostrare il caricatore per ogni chiamata di richiesta http, puoi utilizzare l'interceptor angolare per gestire le chiamate di richiesta http,

ecco un codice di esempio

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

0

Usa solo ng-show e un booleano

Non è necessario utilizzare una direttiva, non è necessario complicarsi.

Ecco il codice da mettere accanto al pulsante di invio o dove vuoi che sia lo spinner:

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

E poi nel tuo controller:

$scope.dataIsLoading = true

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

-1

Ecco la mia soluzione che ritengo sia molto più semplice dell'altra pubblicata qui. Non sono sicuro di quanto sia "carino", ma ha risolto tutti i miei problemi

Ho uno stile CSS chiamato "caricamento"

.loading { display: none; }

L'html per il caricamento div può essere qualsiasi cosa ma ho usato alcune icone FontAwesome e il metodo di rotazione lì:

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

Sugli elementi che vuoi nascondere scrivi semplicemente questo:

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

e nella funzione ho appena impostato questo al caricamento.

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

Sto usando SignalR, quindi nella funzione hubProxy.client.allLocks (quando ha finito attraversando i blocchi) Ho messo

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

Ciò nasconde anche {{someField}} quando la pagina viene caricata poiché sto impostando la classe di caricamento al caricamento e AngularJS la rimuove in seguito.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.