Pokazać GIF tarczy podczas żądania $ http w AngularJS?

234

Korzystam z $httpusługi AngularJS, aby wykonać żądanie Ajax.

W jaki sposób można wyświetlać GIF przędzarki (lub inny wskaźnik zajętości) podczas wykonywania żądania Ajax?

Nie widzę nic ajaxstarteventw dokumentacji AngularJS.

Ajay Beniwal
źródło
2
Jeśli chcesz prosty spinner oparty na HTTP Interceptors, mam do tego moduł kątowy. Wykorzystuje popularny spinner Identified Sham. Spójrz: github.com/harinair/angular-sham-spinner
Hari Gangadharan
1
Napisałem wtyczkę angular-httpshooter , która zwalnia wydarzenie z danymi konfiguracji tuż przed sesją połączenia i wypuszcza kolejne tuż po otrzymaniu odpowiedzi, możesz pisać globalne programy ładujące łapiące te zdarzenia
Siddharth

Odpowiedzi:

88

Oto aktualne przeszłe inkantacje 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']).
//.......

Oto reszta (HTML / CSS) .... za pomocą

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

przełączać to. UWAGA: powyższe stosuje się w module kątowym na początku postu

#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>
hałaśliwy
źródło
19
Zauważ, że możesz użyć angular.element('#mydiv').show()zamiast$('#mydiv').show()
JMaylin,
8
to nie działa, jeśli twoja strona wysyła wiele żądań ajax. Ukryje ładujący gif po zakończeniu pierwszych żądań.
Meeker
38
Zgodnie z najlepszymi praktykami AngularJS (które narusza przyjęte rozwiązanie) nie powinieneś modyfikować DOM poza dyrektywą. Rozważ wywołanie show / hide na elementach z dyrektywy.
Mariusz
7
Nie jestem pewien, czy wiem wystarczająco dużo, aby skomentować ... Ale czy nie najlepszym rozwiązaniem byłoby użycie ng-classdyrektywy zamiast używania JQuery do wyświetlania i ukrywania elementów?
James Heald
2
@JamesHeald ma rację. Nigdy nie powinieneś używać jQuery do manipulacji domem w Angular. Zajrzyj do: klasy ng, ng-if, ng-hide, ng-show itp. Istnieje dyrektywa dotycząca prawie wszystkiego, czego kiedykolwiek będziesz potrzebować.
jKlaus
471

To naprawdę zależy od konkretnego przypadku użycia, ale prosty sposób byłby zgodny z następującym wzorcem:

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

A następnie zareaguj na to w swoim szablonie:

<div class="spinner" ng-show="loading"></div>
<div ng-repeat="item in items>{{item.name}}</div>
Josh David Miller
źródło
223
Jeśli masz wiele żądań ajax w tym samym czasie, możesz zadeklarować loadingjako liczbę całkowitą $scope.loading = 0;, kiedy żądanie zaczyna się $scope.loading++;, a kiedy kończy $scope.loading--;. I nie ma nic do zmiany w szablonie. Tak więc pokrętło jest wyświetlane, gdy trwa co najmniej jedno żądanie, i jest ukryte przez resztę czasu
JMaylin,
16
Prawdopodobnie powinieneś również ustawić ładowanie na false w funkcji zastępowania błędów.
sebnukem
3
@sebnukem Masz rację. To był dość wysoki przykład, ale i tak dodałem go dla jasności. Dziękujemy za opinię!
Josh David Miller,
1
@JMaylin - możesz wtedy użyć $ watch na $ scope. Ładowanie zrobić inne rzeczy
Jason
5
Jeszcze czystszym sposobem, zamiast $scope.loading = falseoddzwaniania powodzenia i niepowodzenia, jest włączenie go .finally(). Wyglądałoby to tak:mySvc.get().then(success, fail).finally(function() { $scope.loading = false; });
Tom
44

Oto wersja używająca directivea ng-hide.

Spowoduje to wyświetlenie modułu ładującego podczas wszystkich połączeń za pośrednictwem $httpusługi angular .

W szablonie:

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

dyrektywa:

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

używając ng-hideklasy na elemencie, możesz uniknąć jquery.


Dostosuj: dodaj interceptor

Jeśli utworzysz przechwytywacz ładujący, możesz pokazać / ukryć moduł ładujący na podstawie warunku.

dyrektywa:

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

przechwytywacz:

  • na przykład: nie pokazuj spinnerkiedyresponse.background === true;
  • Przechwyć requesti / lub responseustaw $rootScope.$broadcast("loader_show");lub$rootScope.$broadcast("loader_hide");

więcej informacji na temat pisania przechwytywacza

punkrockpolly
źródło
@punkrockpolly W moim konkretnym scenariuszu mam dwa osobne komponenty wywołujące usługę. Ale to działa tylko w przypadku jednego z nich. Czy powinienem przekazać parametr lub coś, co sprawi, że będzie to wielokrotnego użytku?
RicardoGonzales,
@razorblade będzie się obracać za każdym razem, gdy wykonasz połączenie za pośrednictwem $httpdowolnej usługi.
punkrockpolly,
31

Jeśli używasz ngResource, atrybut $ resolved obiektu jest użyteczny dla programów ładujących:

Dla zasobu, jak następuje:

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

Możesz połączyć moduł ładujący z atrybutem $ resolved obiektu zasobu:

<div ng-hide="user.$resolved">Loading ...</div>
Alexander Sweeney
źródło
13

Właśnie odkryłem angular-busydyrektywę, która pokazuje mały moduł ładujący w zależności od jakiegoś wywołania asynchronicznego.

Na przykład, jeśli musisz zrobić GET, odwołaj się do obietnicy w swoim $scope,

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

i nazwij to tak:

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

Oto GitHub.

Możesz go również zainstalować za pomocą bower(nie zapomnij zaktualizować zależności projektu):

bower install angular-busy --save
Baltazar
źródło
Nie mogłem znaleźć sposobu, aby „wyłączyć jakiś element” w dokumentacji. Czy możesz wyjaśnić jak? Na przykład chcę wyłączyć przycisk, gdy wyświetla się pokrętło.
c4k
angular-busy nakłada tylko na element maskę, nie sądzę, że możesz wyłączyć przycisk tak, jak chcesz, ale możesz ustawić na nim tło za pomocą pustego szablonu, aby zrobić to wrażenie. Nie jestem native speakerem, przepraszam za zamieszanie :)
Balthazar
Ok, zredagowałem twoją odpowiedź, aby zapobiec sytuacji, w której ktoś miałby takie samo zamieszanie. Francuz mówiący po angielsku do innego francuskiego jest zawsze mylący :)
c4k
5

Jeśli pakujesz swoje połączenia API w ramach usługi / fabryki, możesz tam śledzić licznik załadunku (według odpowiedzi i doskonałej jednoczesnej sugestii @JMaylin) i odwoływać się do licznika załadunku za pomocą dyrektywy. Lub dowolna ich kombinacja.

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

DYREKTYWA W SPRAWIE SPINNERA

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

STOSOWANIE

<div ng-controller="myCtrl" your-api-spinner> ... </div>
drzaus
źródło
5

W przypadku ładowania stron i modów najłatwiejszym sposobem jest użycie ng-showdyrektywy i użycie jednej ze zmiennych danych zakresu. Coś jak:

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

Tutaj, gdy someObjectjest niezdefiniowany, wyświetla się pokrętło. Gdy usługa powróci z danymi i someObjectzostanie zapełniona, pokrętło powróci do stanu ukrytego.

Arnold.Krumins
źródło
3

To chyba najłatwiejszy sposób na dodanie pokrętła: -

Możesz użyć ng-show z tagiem div dowolnego z tych pięknych spinnerów http://tobiasahlin.com/spinkit/ {{To nie jest moja strona}}

i wtedy możesz użyć tego rodzaju logiki

//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
źródło
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>

Dodaj ten 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;
}

I po prostu dodaj kątowo:

$ rootScope.loading = false; $ rootScope.loading = true; -> kiedy kończy się $ http.get.

cabaji99
źródło
2

Udostępniając moją wersję świetnej odpowiedzi z @bulltorious, zaktualizowaną dla nowszych kompilacji kątowych (użyłem wersji 1.5.8 z tym kodem), a także włączyłem pomysł @ JMaylin dotyczący używania licznika, aby był odporny na wiele równoczesnych żądań, a także opcja pominięcia wyświetlania animacji dla żądań trwających krócej niż pewną minimalną liczbę milisekund:

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
źródło
2

Za pomocą przechwytywacza kątowego można zarządzać połączeniami żądań 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
źródło
2

Prosty sposób bez przechwytywaczy i jQuery

Jest to prosty sposób na pokazanie spinnera, który nie wymaga biblioteki innej firmy, przechwytywaczy ani jQuery.

W kontrolerze ustaw i zresetuj 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;
    });
};

W kodzie HTML użyj flagi:

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

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

vm.startingFlaga jest ustawiona truepodczas uruchamiania XHR i wyczyszczone, gdy ukończone, XHR.

georgeawg
źródło
1

Działa to dla mnie dobrze:

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>

Kątowy:

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

Podczas gdy obietnica zwrócona z $ http jest w toku, ng-show oceni ją jako „prawdziwą”. Jest on automatycznie aktualizowany po rozwiązaniu obietnicy ... dokładnie tego chcemy.

wilgotny
źródło
To nie jest dynamiczny sposób na zrobienie tego.
Umair Hamid
Czy możesz wyjaśnić, dlaczego uważasz, że nie jest to dynamiczny sposób na osiągnięcie tego celu. Pomoże to innym czytającym ten post w nauce. Dziękuję Ci.
dank
Mam wiele wniosków w mojej aplikacji. $ scope.req będzie działał tylko dla pojedynczego żądania. Gdy to konkretne żądanie zostanie zakończone, moduł ładujący ukryje się i nie będzie czekał na zakończenie innych żądań. Najlepszym sposobem na osiągnięcie tego jest użycie przechwytywaczy.
Umair Hamid
1

Wykorzystano następujący interpreter, aby pokazać pasek ładowania na żądanie 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
źródło
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);
  }
}
użytkownik1853287
źródło
1
Odpowiedzi zawierające tylko kod nie są zbyt przydatne.
Phantômaxx
0

utwórz dyrektywę za pomocą tego kodu:

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

function toggleLoader(status){
  if(status.length){
    element.addClass('active');
  } else {
    element.removeClass('active');
  }
}
Oleg Ozimok
źródło
0

Innym rozwiązaniem pokazującym ładowanie między różnymi zmianami adresu URL jest:

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

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

A następnie w znacznikach po prostu przełącz pokrętło ng-show="loading".

Jeśli chcesz wyświetlać go w żądaniach ajax, po prostu dodaj, $scope.loading++kiedy żądanie zaczyna się i kiedy kończy, dodaj $scope.loading--.

Chrillewoodz
źródło
0

Możesz także spróbować czegoś takiego:

Utwórz dyrektywę:

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

Następnie dodajesz coś takiego do mainCtrl

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

HTML może wyglądać następująco:

<div class="buttons loader">
    <span class="icon" loader="isLoading()"></span>
</div>
Andurit
źródło
0

Poniższy sposób uwzględni wszystkie żądania i ukryje je dopiero po wykonaniu wszystkich żądań:

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
źródło
0

Ponieważ ostatnio zmieniono funkcjonalność position: fixed , miałem trudności z wyświetleniem programu ładującego gif ponad wszystkimi elementami, więc musiałem użyć wbudowanego jQuery angulara .

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

Kontroler

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

})

Mam nadzieję że to pomoże :)

Magor Menessy
źródło
0

Wszystkie odpowiedzi są lub są skomplikowane, lub trzeba ustawić niektóre zmienne przy każdym żądaniu, co jest bardzo złą praktyką, jeśli znamy koncepcję DRY. Oto prosty przykład przechwytywacza: ustawiam mysz na czekanie, kiedy uruchamia się ajax i ustawiam na auto, kiedy kończy się 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;
      }
    };
  });

Kod należy dodać w konfiguracji aplikacji app.config. Pokazałem, jak zmienić mysz przy ładowaniu, ale tam można pokazać / ukryć dowolną zawartość modułu ładującego lub dodać, usunąć niektóre klasy css, które pokazują moduł ładujący.

Przechwytywacz będzie działał przy każdym wywołaniu ajax, więc nie trzeba tworzyć specjalnych zmiennych boolowskich ($ scope.loading = true / false itp.) Przy każdym wywołaniu http.

Maciej Sikora
źródło
0

Oto moja implementacja, tak prosta jak ng-show i licznik żądań.

Używa nowej usługi dla wszystkich żądań do $ 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--;
        });
    }
} ]);

Następnie możesz użyć bazy ładującej na podstawie liczby bieżących połączeń:

<img data-ng-show="currentCalls > 0" src="images/ajax-loader.gif"/>
Bancarel Valentin
źródło
0

jeśli chcesz wyświetlać moduł ładujący dla każdego wywołania żądania HTTP, możesz użyć przechwytywacza kątowego do zarządzania wywołaniami żądania HTTP,

oto przykładowy kod

<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
źródło
0

Wystarczy użyć ng-show i boolean

Nie ma potrzeby stosowania dyrektywy, nie trzeba się komplikować.

Oto kod, który należy umieścić obok przycisku Prześlij lub w dowolnym miejscu, w którym ma znajdować się pokrętło:

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

A następnie w kontrolerze:

$scope.dataIsLoading = true

let url = '/whatever_Your_URL_Is'
$http.get(url)
.then(function(response) {
  $scope.dataIsLoading = false
})
Adam
źródło
-1

Oto moje rozwiązanie, które moim zdaniem jest znacznie łatwiejsze niż inne zamieszczone tutaj. Nie jestem pewien, jak to jest „ładne”, ale rozwiązało wszystkie moje problemy

Mam styl css o nazwie „ładowanie”

.loading { display: none; }

HTML dla div ładowania może być dowolny, ale użyłem tam kilku ikon FontAwesome i metody wirowania:

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

Na elementach, które chcesz ukryć, po prostu napisz to:

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

i w funkcji właśnie ustawiłem to na obciążenie.

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

Korzystam z SignalR, więc w funkcji hubProxy.client.allLocks (po zakończeniu przechodzenia przez blokady) umieszczam

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

Ukrywa to również {{someField}} podczas ładowania strony, ponieważ ustawiam klasę ładowania na ładowanie, a AngularJS usuwa ją później.

stibay
źródło