Ekran ładowania Angularjs na żądanie Ajax

117

Używając Angularjs, muszę pokazać ekran ładowania (prosty spinner), dopóki żądanie AJAX nie zostanie zakończone. Zaproponuj dowolny pomysł za pomocą fragmentu kodu.

Badhrinath Canessane
źródło
4
zajrzyj na stackoverflow.com/questions/15033195/…
Ajay Beniwal,
Najlepszą praktyką jest używanie, $httpProvider.interceptorsponieważ może obsługiwać, kiedy żądanie AJAX zaczyna się i kończy. Dlatego $watchnie ma już potrzeby wykrywania początku i końca wywołania ajax.
Frank Myat Czw
co powiesz na zakup mojej fabrycznej hulajnogi kątowej , która zapewnia lepszą kontrolę nad ładowarkami i interfejsem zamrażania
Siddharth

Odpowiedzi:

210

Zamiast konfigurować zmienną zakresu, aby wskazywać stan ładowania danych, lepiej jest mieć dyrektywę, która zrobi wszystko za Ciebie:

angular.module('directive.loading', [])

    .directive('loading',   ['$http' ,function ($http)
    {
        return {
            restrict: 'A',
            link: function (scope, elm, attrs)
            {
                scope.isLoading = function () {
                    return $http.pendingRequests.length > 0;
                };

                scope.$watch(scope.isLoading, function (v)
                {
                    if(v){
                        elm.show();
                    }else{
                        elm.hide();
                    }
                });
            }
        };

    }]);

Dzięki tej dyrektywie wszystko, co musisz zrobić, to nadać dowolnemu elementowi animacji ładowania atrybutu „ładowanie”:

<div class="loading-spiner-holder" data-loading ><div class="loading-spiner"><img src="..." /></div></div>

Na stronie możesz mieć wiele pokręteł ładowania. gdzie i jak rozmieścić te przędzarki zależy od Ciebie, a dyrektywa po prostu włączy / wyłączy je automatycznie.

David Lin
źródło
tutaj dyrektywa "loading" jest wywoływana dwukrotnie, z $ location.path (). Najpierw z bieżącą stroną, a następnie ze stroną docelową. Jaki powinien być powód?
Sanjay D,
Doskonały. Możesz także użyć przełącznika jquery na zegarku: elm.toggle (v). Mam monitor sesji $ timeout i dla mnie muszę pokazać spinner dopiero po pół sekundy. Zaimplementuję css z przejściem, aby wolniej pokazywać spinner.
Joao Polo
7
Jeśli otrzymujesz błąd „elm.show () nie jest funkcją”, musisz dodać jquery przed załadowaniem angular.
morpheus 05
Sposób, w jaki robisz zakres. Zegarek nie działa dla mnie. Musiałem to zrobić tak, jak robi to jzm (używając „” wokół nazwy i pomijając przedrostek zakresu). Jakieś pomysły, dlaczego?
KingOfHypocrites
3
Co jeśli potrzebuję asynchronizacji. ładować wiele różnych obszarów?
Vadim Ferderer
56

Oto przykład. Używa prostej metody ng-show z wartością bool.

HTML

<div ng-show="loading" class="loading"><img src="...">LOADING...</div>
<div ng-repeat="car in cars">
  <li>{{car.name}}</li>
</div>
<button ng-click="clickMe()" class="btn btn-primary">CLICK ME</button>

ANGULARJS

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

Oczywiście możesz przenieść kod html pola ładowania do dyrektywy, a następnie użyć $ watch na $ scope.loading. W którym to przypadku:

HTML :

<loading></loading>

DYREKTYWA ANGULARJS :

  .directive('loading', function () {
      return {
        restrict: 'E',
        replace:true,
        template: '<div class="loading"><img src="..."/>LOADING...</div>',
        link: function (scope, element, attr) {
              scope.$watch('loading', function (val) {
                  if (val)
                      $(element).show();
                  else
                      $(element).hide();
              });
        }
      }
  })

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

mnsr
źródło
Sposób, w jaki robisz scope.watch, działa dla mnie, podczas gdy zaakceptowane podejście nie działa.
KingOfHypocrites
@jzm Takie świetne rozwiązanie 😊 niestety to rozwiązanie nie działa ui-router, nie wiem czemu
pn.
Zrobiłem to w jednym z moich projektów działało dobrze, w innym projekcie myślę, że z powodu ciężkiego żądania ajax loading=truenie jest ustawiane
alamnaryab
21

Używam ngProgress do tego.

Dodaj „ngProgress” do swoich zależności po dołączeniu plików skryptów / css do kodu HTML. Gdy to zrobisz, możesz ustawić coś takiego, co będzie uruchamiane po wykryciu zmiany trasy.

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

W przypadku żądań AJAX możesz zrobić coś takiego:

$scope.getLatest = function () {
    ngProgress.start();

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

Pamiętaj tylko o dodaniu „ngProgress” do zależności kontrolerów, zanim to zrobisz. A jeśli wykonujesz wiele żądań AJAX, użyj zmiennej przyrostowej w głównym zakresie aplikacji, aby śledzić zakończenie żądań AJAX przed wywołaniem „ngProgress.complete ();”.

Nej Kutcharian
źródło
15

używanie pendingRequests nie jest poprawne, ponieważ jak wspomniano w dokumentacji Angular, ta właściwość jest przeznaczona głównie do celów debugowania.

Zalecam użycie przechwytywacza, aby dowiedzieć się, czy jest jakieś aktywne połączenie asynchroniczne.

module.config(['$httpProvider', function ($httpProvider) {
    $httpProvider.interceptors.push(function ($q, $rootScope) {
        if ($rootScope.activeCalls == undefined) {
            $rootScope.activeCalls = 0;
        }

        return {
            request: function (config) {
                $rootScope.activeCalls += 1;
                return config;
            },
            requestError: function (rejection) {
                $rootScope.activeCalls -= 1;
                return rejection;
            },
            response: function (response) {
                $rootScope.activeCalls -= 1;
                return response;
            },
            responseError: function (rejection) {
                $rootScope.activeCalls -= 1;
                return rejection;
            }
        };
    });
}]);

a następnie sprawdź, czy activeCalls ma wartość zero, czy nie, w dyrektywie za pomocą $ watch.

module.directive('loadingSpinner', function ($http) {
    return {
        restrict: 'A',
        replace: true,
        template: '<div class="loader unixloader" data-initialize="loader" data-delay="500"></div>',
        link: function (scope, element, attrs) {

            scope.$watch('activeCalls', function (newVal, oldVal) {
                if (newVal == 0) {
                    $(element).hide();
                }
                else {
                    $(element).show();
                }
            });
        }
    };
});
Mahdi K.
źródło
7

Najlepszym sposobem na to jest użycie przechwytywaczy odpowiedzi wraz z dyrektywą niestandardową . Proces można dodatkowo usprawnić za pomocą mechanizmu pub / sub przy użyciu metod $ rootScope. $ Broadcast i $ rootScope. $ On .

Ponieważ cały proces jest udokumentowany w dobrze napisanym artykule na blogu , nie zamierzam już tego tutaj powtarzać. Zapoznaj się z tym artykułem, aby znaleźć potrzebną implementację.

MN Islam Shihan
źródło
4

W nawiązaniu do tej odpowiedzi

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

Dla mnie to najlepsze rozwiązanie, ale jest sposób na uniknięcie używania jQuery.

.directive('loading', function () {
      return {
        restrict: 'E',
        replace:true,
        template: '<div class="loading"><img src="http://www.nasa.gov/multimedia/videogallery/ajax-loader.gif" width="20" height="20" />LOADING...</div>',
        link: function (scope, element, attr) {
              scope.$watch('loading', function (val) {
                  if (val)
                      scope.loadingStatus = 'true';
                  else
                      scope.loadingStatus = 'false';
              });
        }
      }
  })

  .controller('myController', function($scope, $http) {
      $scope.cars = [];
      
      $scope.clickMe = function() {
        scope.loadingStatus = 'true'
        $http.get('test.json')
          .success(function(data) {
            $scope.cars = data[0].cars;
            $scope.loadingStatus = 'false';
        });
      }
      
  });
<body ng-app="myApp" ng-controller="myController" ng-init="loadingStatus='true'">
        <loading ng-show="loadingStatus" ></loading>
  
        <div ng-repeat="car in cars">
          <li>{{car.name}}</li>
        </div>
        <button ng-click="clickMe()" class="btn btn-primary">CLICK ME</button>
  
</body>

Musisz zamienić $ (element) .show (); i (element) .show (); with $ scope.loadingStatus = 'true'; i $ scope.loadingStatus = 'false';

Następnie musisz użyć tej zmiennej, aby ustawić atrybut ng-show elementu.

Karsus
źródło
4

Implementacja maszynopisu i kątów

dyrektywa

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

Zakres

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

Szablon

<div id="activityspinner" ng-show="canShow" class="show" data-busyindicator>
</div>

CSS
#activityspinner
{
    display : none;
}
#activityspinner.show {
    display : block;
    position : fixed;
    z-index: 100;
    background-image : url('data:image/gif;base64,R0lGODlhNgA3APMAAPz8/GZmZqysrHV1dW1tbeXl5ZeXl+fn59nZ2ZCQkLa2tgAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAANgA3AAAEzBDISau9OOvNu/9gKI5kaZ4lkhBEgqCnws6EApMITb93uOqsRC8EpA1Bxdnx8wMKl51ckXcsGFiGAkamsy0LA9pAe1EFqRbBYCAYXXUGk4DWJhZN4dlAlMSLRW80cSVzM3UgB3ksAwcnamwkB28GjVCWl5iZmpucnZ4cj4eWoRqFLKJHpgSoFIoEe5ausBeyl7UYqqw9uaVrukOkn8LDxMXGx8ibwY6+JLxydCO3JdMg1dJ/Is+E0SPLcs3Jnt/F28XXw+jC5uXh4u89EQAh+QQJCgAAACwAAAAANgA3AAAEzhDISau9OOvNu/9gKI5kaZ5oqhYGQRiFWhaD6w6xLLa2a+iiXg8YEtqIIF7vh/QcarbB4YJIuBKIpuTAM0wtCqNiJBgMBCaE0ZUFCXpoknWdCEFvpfURdCcM8noEIW82cSNzRnWDZoYjamttWhphQmOSHFVXkZecnZ6foKFujJdlZxqELo1AqQSrFH1/TbEZtLM9shetrzK7qKSSpryixMXGx8jJyifCKc1kcMzRIrYl1Xy4J9cfvibdIs/MwMue4cffxtvE6qLoxubk8ScRACH5BAkKAAAALAAAAAA2ADcAAATOEMhJq7046827/2AojmRpnmiqrqwwDAJbCkRNxLI42MSQ6zzfD0Sz4YYfFwyZKxhqhgJJeSQVdraBNFSsVUVPHsEAzJrEtnJNSELXRN2bKcwjw19f0QG7PjA7B2EGfn+FhoeIiYoSCAk1CQiLFQpoChlUQwhuBJEWcXkpjm4JF3w9P5tvFqZsLKkEF58/omiksXiZm52SlGKWkhONj7vAxcbHyMkTmCjMcDygRNAjrCfVaqcm11zTJrIjzt64yojhxd/G28XqwOjG5uTxJhEAIfkECQoAAAAsAAAAADYANwAABM0QyEmrvTjrzbv/YCiOZGmeaKqurDAMAlsKRE3EsjjYxJDrPN8PRLPhhh8XDMk0KY/OF5TIm4qKNWtnZxOWuDUvCNw7kcXJ6gl7Iz1T76Z8Tq/b7/i8qmCoGQoacT8FZ4AXbFopfTwEBhhnQ4w2j0GRkgQYiEOLPI6ZUkgHZwd6EweLBqSlq6ytricICTUJCKwKkgojgiMIlwS1VEYlspcJIZAkvjXHlcnKIZokxJLG0KAlvZfAebeMuUi7FbGz2z/Rq8jozavn7Nev8CsRACH5BAkKAAAALAAAAAA2ADcAAATLEMhJq7046827/2AojmRpnmiqrqwwDAJbCkRNxLI42MSQ6zzfD0Sz4YYfFwzJNCmPzheUyJuKijVrZ2cTlrg1LwjcO5HFyeoJeyM9U++mfE6v2+/4PD6O5F/YWiqAGWdIhRiHP4kWg0ONGH4/kXqUlZaXmJlMBQY1BgVuUicFZ6AhjyOdPAQGQF0mqzauYbCxBFdqJao8rVeiGQgJNQkIFwdnB0MKsQrGqgbJPwi2BMV5wrYJetQ129x62LHaedO21nnLq82VwcPnIhEAIfkECQoAAAAsAAAAADYANwAABMwQyEmrvTjrzbv/YCiOZGmeaKqurDAMAlsKRE3EsjjYxJDrPN8PRLPhhh8XDMk0KY/OF5TIm4qKNWtnZxOWuDUvCNw7kcXJ6gl7Iz1T76Z8Tq/b7/g8Po7kX9haKoAZZ0iFGIc/iRaDQ40Yfj+RepSVlpeYAAgJNQkIlgo8NQqUCKI2nzNSIpynBAkzaiCuNl9BIbQ1tl0hraewbrIfpq6pbqsioaKkFwUGNQYFSJudxhUFZ9KUz6IGlbTfrpXcPN6UB2cHlgfcBuqZKBEAIfkECQoAAAAsAAAAADYANwAABMwQyEmrvTjrzbv/YCiOZGmeaKqurDAMAlsKRE3EsjjYxJDrPN8PRLPhhh8XDMk0KY/OF5TIm4qKNWtnZxOWuDUvCNw7kcXJ6gl7Iz1T76Z8Tq/b7yJEopZA4CsKPDUKfxIIgjZ+P3EWe4gECYtqFo82P2cXlTWXQReOiJE5bFqHj4qiUhmBgoSFho59rrKztLVMBQY1BgWzBWe8UUsiuYIGTpMglSaYIcpfnSHEPMYzyB8HZwdrqSMHxAbath2MsqO0zLLorua05OLvJxEAIfkECQoAAAAsAAAAADYANwAABMwQyEmrvTjrzbv/YCiOZGmeaKqurDAMAlsKRE3EsjjYxJDrPN8PRLPhfohELYHQuGBDgIJXU0Q5CKqtOXsdP0otITHjfTtiW2lnE37StXUwFNaSScXaGZvm4r0jU1RWV1hhTIWJiouMjVcFBjUGBY4WBWw1A5RDT3sTkVQGnGYYaUOYPaVip3MXoDyiP3k3GAeoAwdRnRoHoAa5lcHCw8TFxscduyjKIrOeRKRAbSe3I9Um1yHOJ9sjzCbfyInhwt3E2cPo5dHF5OLvJREAOwAAAAAAAAAAAA==') 
    -ms-opacity : 0.4;
    opacity : 0.4;
    background-repeat : no-repeat;
    background-position : center;
    left : 0;
    bottom : 0;
    right : 0;
    top : 0;
}
Code-EZ
źródło
3

Jeśli używasz Restangular (co jest niesamowite), możesz utworzyć animację podczas wywołań API. Oto moje rozwiązanie. Dodaj przechwytywacz odpowiedzi i przechwytywacz żądania, który wysyła transmisję głównego zakresu. Następnie utwórz dyrektywę, która nasłuchuje tej odpowiedzi i żądania .:

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

Oto dyrektywa:

        angular.module('mean.system')
  .directive('smartLoadingIndicator', function($rootScope) {
    return {
      restrict: 'AE',
      template: '<div ng-show="isAPICalling"><p><i class="fa fa-gear fa-4x fa-spin"></i>&nbsp;Loading</p></div>',
      replace: true,
      link: function(scope, elem, attrs) {
        scope.isAPICalling = false;

        $rootScope.$on('apiRequest', function() {
          scope.isAPICalling = true;
        });
        $rootScope.$on('apiResponse', function() {
          scope.isAPICalling = false;
        });
      }
    };
  })
;
Enkode
źródło
Każda odpowiedź API zabije animację. Powinieneś przechowywać dodatkowe informacje o żądaniu - odpowiedzi i reagować tylko na tę odpowiedź, która została zainicjowana przez żądanie.
Krzysztof Safjanowski 20.04.2015
3

Uwzględnij to w swoim „app.config”:

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

I dodaj ten kod:

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

            }
            if($rootScope.ActiveAjaxConectionsWithouthNotifications<=0){
                $rootScope.ActiveAjaxConectionsWithouthNotifications=0;
                $('#loading_view').hide();

            }


    };
return {
    'request': function(config) {
        checker(config,"request");
        return config;
    },
   'requestError': function(rejection) {
       checker(rejection.config,"request");
      return $q.reject(rejection);
    },
    'response': function(response) {
         checker(response.config,"response");
      return response;
    },
   'responseError': function(rejection) {
        checker(rejection.config,"response");
      return $q.reject(rejection);
    }
  };
});
BratisLatas
źródło
2

Użyj angular-busy :

Dodaj cgBusy as do swojej aplikacji / modułu:

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

Dodaj swoją obietnicę do scope:

function MyCtrl($http, User) {
  //using $http
  this.isBusy = $http.get('...');
  //if you have a User class based on $resource
  this.isBusy = User.$save();
}

W swoim szablonie HTML:

<div cg-busy="$ctrl.isBusy"></div>
krl
źródło
2

Jest też fajne demo, które pokazuje, jak możesz wykorzystać Angularjsanimację w swoim projekcie.

Link jest tutaj (patrz lewy górny róg) .

To open source. Oto link do pobrania

A oto link do samouczka ;

Chodzi mi o to, pobierz pliki źródłowe, a następnie zobacz, jak zaimplementowały spinner. Mogliby użyć trochę lepszego podejścia. Więc sprawdź ten projekt.

Idrees Khan
źródło
1

Tutaj prosty przykład przechwytywacza, ustawiam mysz na oczekiwanie, gdy zaczyna się Ajax i ustawiam ją na auto, gdy 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ć stan myszy przy ładowaniu, ale tam można pokazać / ukryć zawartość dowolnego modułu ładującego lub dodać, usunąć niektóre klasy css, które pokazują program ładujący.

Interceptor będzie działał przy każdym wywołaniu Ajax, więc nie ma potrzeby tworzenia specjalnych zmiennych boolowskich ($ scope.loading = true / false itp.) Przy każdym wywołaniu http.

Interceptor używa wbudowanego w kątowe jqLite https://docs.angularjs.org/api/ng/function/angular.element, więc nie jest potrzebne Jquery.

Maciej Sikora
źródło
1

Utwórz dyrektywę z atrybutami show i size (możesz również dodać więcej)

    app.directive('loader',function(){
    return {
    restrict:'EA',
    scope:{
        show : '@',
      size : '@'
    },
    template : '<div class="loader-container"><div class="loader" ng-if="show" ng-class="size"></div></div>'
  }
})

aw html jako

 <loader show="{{loader1}}" size="sm"></loader>

W zmiennej show przekazuj wartość true, gdy obietnica jest uruchomiona i ustaw ją jako fałszywą, gdy żądanie zostanie zakończone. Aktywne demo - przykładowe demo dyrektywy Angular Loader w JsFiddle

Partha Roy
źródło
0

Oparłem się trochę na odpowiedzi @ DavidLin, aby ją uprościć - usuwając w dyrektywie wszelkie zależności od jQuery . Mogę potwierdzić, że to działa, ponieważ używam go w aplikacji produkcyjnej

function AjaxLoadingOverlay($http) {

    return {
        restrict: 'A',
        link: function ($scope, $element, $attributes) {

            $scope.loadingOverlay = false;

            $scope.isLoading = function () {
                return $http.pendingRequests.length > 0;
            };

            $scope.$watch($scope.isLoading, function (isLoading) {
                $scope.loadingOverlay = isLoading;
            });
        }
    };
}   

Używam ng-showzamiast wywołania jQuery, aby ukryć / pokazać plik <div>.

Oto, <div>który umieściłem tuż pod otwierającym <body>tagiem:

<div ajax-loading-overlay class="loading-overlay" ng-show="loadingOverlay">
    <img src="Resources/Images/LoadingAnimation.gif" />
</div>

A oto CSS, który zapewnia nakładkę do blokowania interfejsu użytkownika podczas wywołania $ http:

.loading-overlay {
    position: fixed;
    z-index: 999;
    height: 2em;
    width: 2em;
    overflow: show;
    margin: auto;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
}

.loading-overlay:before {
    content: '';
    display: block;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0,0,0,0.3);
}

/* :not(:required) hides these rules from IE9 and below */
.loading-overlay:not(:required) {
    font: 0/0 a;
    color: transparent;
    text-shadow: none;
    background-color: transparent;
    border: 0;
}

Kredyt CSS trafia do @Steve Seeger's - jego post: https://stackoverflow.com/a/35470281/335545

Berno
źródło
0

Możesz dodać warunek, a następnie zmienić go za pomocą zakresu głównego. Przed żądaniem AJAX po prostu wywołujesz $ rootScope. $ Emit ('stopLoader');

angular.module('directive.loading', [])
        .directive('loading',   ['$http', '$rootScope',function ($http, $rootScope)
        {
            return {
                restrict: 'A',
                link: function (scope, elm, attrs)
                {
                    scope.isNoLoadingForced = false;
                    scope.isLoading = function () {
                        return $http.pendingRequests.length > 0 && scope.isNoLoadingForced;
                    };

                    $rootScope.$on('stopLoader', function(){
                        scope.isNoLoadingForced = true;
                    })

                    scope.$watch(scope.isLoading, function (v)
                    {
                        if(v){
                            elm.show();
                        }else{
                            elm.hide();
                        }
                    });
                }
            };

        }]);

To zdecydowanie nie jest najlepsze rozwiązanie, ale nadal działa.

Użytkownik_3535
źródło