Spinner-GIF während einer $ http-Anfrage in AngularJS anzeigen?

234

Ich benutze den $httpDienst von AngularJS, um eine Ajax-Anfrage zu stellen.

Wie kann ein Spinner-GIF (oder eine andere Art von Besetztanzeige) angezeigt werden, während die Ajax-Anforderung ausgeführt wird?

Ich sehe so etwas ajaxstarteventin der AngularJS-Dokumentation nicht.

Ajay Beniwal
quelle
2
Wenn Sie einen einfachen Spinner möchten, der auf HTTP-Interceptors basiert, habe ich dafür ein Winkelmodul. Es verwendet den beliebten Identified Sham-Spinner. Werfen
Hari Gangadharan
1
Ich habe ein Plugin Angular-httpshooter geschrieben , das ein Ereignis mit Konfigurationsdaten kurz vor dem Aufrufen des Anrufs freigibt und ein anderes kurz nach Erhalt der Resposne freigibt. Sie können globale Loader schreiben, die diese Ereignisse abfangen
Siddharth,

Antworten:

88

Hier sind die aktuellen früheren AngularJS-Beschwörungsformeln:

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

Hier ist der Rest davon (HTML / CSS) .... mit

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

um es umzuschalten. HINWEIS: Das Obige wird im Winkelmodul zu Beginn des Beitrags verwendet

#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>
bulltorious
quelle
19
Beachten Sie, dass Sie angular.element('#mydiv').show()anstelle von$('#mydiv').show()
JMaylin
8
Dies funktioniert nicht, wenn Ihre Seite mehrere Ajax-Anfragen stellt. Das Lade-GIF wird ausgeblendet, nachdem die ersten Anforderungen beendet wurden.
Meeker
38
Gemäß den Best Practices von AngularJS (gegen die die akzeptierte Lösung verstößt) sollten Sie das DOM nicht außerhalb einer Richtlinie ändern. Ziehen Sie in Betracht, Elemente innerhalb einer Direktive ein- oder auszublenden.
Mariusz
7
Ich bin mir nicht sicher, ob ich genug weiß, um Kommentare abzugeben ... Aber wäre es nicht der richtige Weg, dies zu tun, die ng-classDirektive zu verwenden, anstatt JQuery zum Ein- und Ausblenden von Elementen zu verwenden?
James Heald
2
@JamesHeald ist korrekt. Sie sollten jQuery niemals für Dom-Manipulationen in Angular verwenden müssen. Schauen Sie sich Folgendes an: ng-class, ng-if, ng-hide, ng-show usw. Es gibt eine Direktive für fast alles, was Sie jemals brauchen werden.
jKlaus
471

Dies hängt wirklich von Ihrem spezifischen Anwendungsfall ab, aber ein einfacher Weg würde einem Muster wie diesem folgen:

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

Und dann reagieren Sie in Ihrer Vorlage darauf:

<div class="spinner" ng-show="loading"></div>
<div ng-repeat="item in items>{{item.name}}</div>
Josh David Miller
quelle
223
Wenn Sie mehrere Ajax-Anforderungen gleichzeitig haben, können Sie loadingals Ganzzahl deklarieren $scope.loading = 0;, wann eine Anforderung beginnt $scope.loading++;und wann sie endet $scope.loading--;. An der Vorlage gibt es nichts zu ändern. Der Spinner wird also angezeigt, wenn mindestens eine Anforderung ausgeführt wird, und ist für den Rest der Zeit
ausgeblendet
16
Sie sollten das Laden wahrscheinlich auch in der Fehler-Fallback-Funktion auf false setzen.
sebnukem
3
@sebnukem Richtig, du bist. Dies war ein ziemlich hochrangiges Beispiel, aber ich habe es aus Gründen der Klarheit trotzdem hinzugefügt. Danke für die Rückmeldung!
Josh David Miller
1
@JMaylin - Sie können dann $ watch auf $ scope.loading verwenden, um andere Dinge zu tun
Jason
5
Ein noch saubererer Weg, anstatt $scope.loading = falsesowohl den erfolgreichen als auch den fehlgeschlagenen Rückruf einzugeben, besteht darin, ihn in den .finally(). Es würde aussehen wie:mySvc.get().then(success, fail).finally(function() { $scope.loading = false; });
Tom
44

Hier ist eine Version mit einem directiveund ng-hide.

Dies zeigt den Lader während aller Anrufe über den Winkeldienst an $http.

In der Vorlage:

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

Richtlinie:

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

Durch die Verwendung der ng-hideKlasse für das Element können Sie jquery vermeiden.


Anpassen: Hinzufügen eines interceptor

Wenn Sie einen Ladeabfangjäger erstellen, können Sie den Lader basierend auf einer Bedingung ein- / ausblenden.

Richtlinie:

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

Abfangjäger:

  • Zum Beispiel: Nicht anzeigen spinnerwannresponse.background === true;
  • Abfangen requestund / oder responseeinstellen $rootScope.$broadcast("loader_show");oder$rootScope.$broadcast("loader_hide");

Weitere Informationen zum Schreiben eines Abfangjägers

Punkrockpolly
quelle
@punkrockpolly In meinem speziellen Szenario habe ich zwei einzelne Komponenten, die einen Dienst aufrufen. Dies funktioniert jedoch nur für einen von ihnen. Soll ich einen Parameter oder etwas übergeben, um dies wiederverwendbar zu machen?
RicardoGonzales
@razorblade Es dreht sich immer dann, wenn Sie $httpin einem beliebigen Dienst einen Anruf tätigen .
Punkrockpolly
31

Wenn Sie ngResource verwenden, ist das $ aufgelöste Attribut eines Objekts für Lader nützlich:

Für eine Ressource wie folgt:

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

Sie können einen Loader mit dem Attribut $ resolve des Ressourcenobjekts verknüpfen:

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

Ich habe gerade die angular-busyDirektive entdeckt, die je nach asynchronem Aufruf einen kleinen Loader anzeigt.

Wenn Sie beispielsweise ein machen müssen GET, verweisen Sie auf das Versprechen in Ihrem $scope,

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

und nenne es so:

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

Hier ist die GitHub.

Sie können es auch installieren mit bower(vergessen Sie nicht, Ihre Projektabhängigkeiten zu aktualisieren):

bower install angular-busy --save
Balthazar
quelle
Ich konnte in der Dokumentation nicht finden, wie "ein Element deaktiviert" werden kann. Können Sie erklären, wie? Zum Beispiel möchte ich eine Schaltfläche deaktivieren, während der Spinner angezeigt wird.
c4k
eckig-beschäftigt setzen Sie nur eine Maske auf Ihr Element. Ich glaube nicht, dass Sie eine Schaltfläche deaktivieren können, wie Sie möchten, aber Sie können den Hintergrund darauf mit einer leeren Vorlage einstellen, um diesen Eindruck zu vermitteln. Ich bin kein Muttersprachler, entschuldige die Verwirrung :)
Balthazar
Ok, ich habe Ihre Antwort bearbeitet, um zu verhindern, dass jemand anderes die gleiche Verwirrung hat. Ein Französisch sprechendes Englisch zu einem anderen Französisch ist immer verwirrend :)
c4k
5

Wenn Sie Ihre API-Anrufe innerhalb eines Dienstes / einer Fabrik verpacken, können Sie dort den Ladezähler verfolgen (pro Antwort und ausgezeichneter gleichzeitiger Vorschlag von @JMaylin) und den Ladezähler über eine Direktive referenzieren. Oder irgendeine Kombination davon.

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

SPINNER DIRECTIVE

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

VERWENDUNG

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

Für Seitenladevorgänge und Modalitäten ist es am einfachsten, die ng-showDirektive und eine der Bereichsdatenvariablen zu verwenden. Etwas wie:

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

Hier someObjectwird der Spinner angezeigt , solange er nicht definiert ist. Sobald der Dienst mit Daten zurückkehrt und someObjectgefüllt ist, kehrt der Spinner in seinen verborgenen Zustand zurück.

Arnold.Krumins
quelle
3

Dies ist der einfachste Weg, einen Spinner hinzuzufügen, denke ich: -

Sie können ng-show mit dem div-Tag eines dieser schönen Spinner verwenden. Http://tobiasahlin.com/spinkit/ {{Dies ist nicht meine Seite}}

und dann können Sie diese Art von Logik verwenden

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

Fügen Sie dieses CSS hinzu:

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

Und nur in Ihrem Winkel hinzufügen:

$ rootScope.loading = false; $ rootScope.loading = true; -> wenn $ http.get endet.

cabaji99
quelle
2

Teilen Sie meine Version der großartigen Antwort von @bulltorious, die für neuere Winkel-Builds aktualisiert wurde (ich habe Version 1.5.8 mit diesem Code verwendet), und haben Sie auch die Idee von @ JMaylin aufgenommen, einen Zähler zu verwenden, um für mehrere gleichzeitige Anforderungen robust zu sein Option zum Überspringen der Anzeige der Animation für Anforderungen, die weniger als eine Mindestanzahl von Millisekunden dauern:

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

Sie können Angular Interceptor verwenden, um HTTP-Anforderungsaufrufe zu verwalten

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

Einfacher Weg ohne Interceptors oder jQuery

Dies ist eine einfache Möglichkeit, einen Spinner anzuzeigen, für den keine Bibliothek, Intercepter oder jQuery eines Drittanbieters erforderlich sind.

Setzen und setzen Sie in der Steuerung ein 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;
    });
};

Verwenden Sie im HTML das Flag:

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

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

Das vm.startingFlag wird gesetzt, truewenn die XHR gestartet wird, und gelöscht, wenn die XHR abgeschlossen ist.

georgeawg
quelle
1

Das funktioniert gut für mich:

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>

Winkel:

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

Während das von $ http zurückgegebene Versprechen noch aussteht, wird ng-show es als "wahr" bewerten. Dies wird automatisch aktualisiert, sobald das Versprechen gelöst ist ... genau das, was wir wollen.

danke
quelle
Dies ist nicht der dynamische Weg, dies zu tun.
Umair Hamid
Könnten Sie bitte erklären, warum Sie glauben, dass dies kein dynamischer Weg ist, um dieses Ziel zu erreichen? Dies wird anderen helfen, die diesen Beitrag lesen. Danke dir.
Dank
Ich habe mehrere Anfragen in meiner Bewerbung. $ scope.req funktioniert nur für einzelne Anforderungen. Wenn diese bestimmte Anforderung abgeschlossen ist, wird der vollständige Ladeprogramm ausgeblendet und wartet nicht darauf, andere Anforderungen abzuschließen. Der beste Weg, dies zu erreichen, ist die Verwendung von Abfangjägern.
Umair Hamid
1

Wird nach folgendem Intercepter verwendet, um die Ladeleiste auf http-Anfrage anzuzeigen

'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
quelle
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);
  }
}
user1853287
quelle
1
Nur-Code-Antworten sind nicht sehr nützlich.
Phantômaxx
0

Erstellen Sie eine Direktive mit diesem Code:

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

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

Eine andere Lösung, um das Laden zwischen verschiedenen URL-Änderungen anzuzeigen, ist:

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

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

Und dann im Markup einfach den Spinner mit umschalten ng-show="loading".

Wenn Sie es auf Ajax-Anfragen anzeigen möchten, fügen Sie es einfach hinzu, $scope.loading++wenn die Anfrage beginnt und wenn sie endet $scope.loading--.

Chrillewoodz
quelle
0

Sie können auch so etwas ausprobieren:

Direktive erstellen:

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

Dann fügen Sie mainCtrl so etwas hinzu

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

Und HTML kann so aussehen:

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

Auf folgende Weise werden alle Anforderungen zur Kenntnis genommen und erst ausgeblendet, wenn alle Anforderungen erledigt sind:

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

Da sich die Funktionalität von position: fixed kürzlich geändert hat, hatte ich Schwierigkeiten, den GIF-Loader über allen Elementen anzuzeigen, sodass ich Angulars eingebaute jQuery verwenden musste .

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

Regler

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

})

Hoffe das hilft :)

Magor Menessy
quelle
0

Alle Antworten sind oder zu kompliziert oder müssen bei jeder Anfrage einige Variablen setzen, was sehr falsch ist, wenn wir das DRY-Konzept kennen. In diesem einfachen Interceptor-Beispiel setze ich die Maus auf Warten, wenn Ajax startet, und auf Auto, wenn Ajax endet.

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

Code muss in der Anwendungskonfiguration hinzugefügt werden app.config. Ich habe gezeigt, wie man die Maus beim Laden ändert, aber dort ist es möglich, Loader-Inhalte anzuzeigen / auszublenden oder einige CSS-Klassen hinzuzufügen, die den Loader anzeigen, zu entfernen.

Interceptor wird bei jedem Ajax-Aufruf ausgeführt, sodass bei jedem http-Aufruf keine speziellen booleschen Variablen ($ scope.loading = true / false usw.) erstellt werden müssen.

Maciej Sikora
quelle
0

Hier ist meine Implementierung, so einfach wie eine ng-show und ein Anforderungszähler.

Es wird ein neuer Dienst für alle Anfragen an $ http verwendet:

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

Und dann können Sie Ihre Loader-Basis anhand der Anzahl der aktuellen Anrufe verwenden:

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

Wenn Sie den Loader für jeden http-Anforderungsaufruf anzeigen möchten, können Sie den Angular Interceptor verwenden, um http-Anforderungsaufrufe zu verwalten.

Hier ist ein Beispielcode

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

Verwenden Sie einfach ng-show und einen Booleschen Wert

Keine Notwendigkeit, eine Richtlinie zu verwenden, keine Notwendigkeit, kompliziert zu werden.

Hier ist der Code, den Sie neben dem Senden-Button oder wo immer Sie möchten, dass sich der Spinner befindet:

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

Und dann in Ihrem Controller:

$scope.dataIsLoading = true

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

Hier ist meine Lösung, die meiner Meinung nach viel einfacher ist als die andere, die hier gepostet wurde. Ich bin mir nicht sicher, wie "hübsch" es ist, aber es hat alle meine Probleme gelöst

Ich habe einen CSS-Stil namens "Laden"

.loading { display: none; }

Das HTML für das Laden von div kann beliebig sein, aber ich habe dort einige FontAwesome-Symbole und die Spin-Methode verwendet:

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

Auf die Elemente, die Sie ausblenden möchten, schreiben Sie einfach Folgendes:

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

und in der Funktion setze ich dies nur auf Last.

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

Ich benutze SignalR, also in der Funktion hubProxy.client.allLocks (wenn es durch die Sperren geht), die ich gerade stelle

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

Dadurch wird auch das {{someField}} ausgeblendet, wenn die Seite geladen wird, da ich die Ladeklasse auf Laden setze und AngularJS sie anschließend entfernt.

stibay
quelle