[angularjs] Show spinner GIF during an $http request in AngularJS?

I am using the $http service of AngularJS to make an Ajax request.

How can a spinner GIF (or another type of busy indicator) be shown while the Ajax request is executing?

I don't see anything like an ajaxstartevent in the AngularJS documentation.

This question is related to angularjs ajax busyindicator

The answer is


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

Used following intercepter to show loading bar on http request

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

Here is my solution which i feel is alot easer that the other posted here. Not sure how "pretty" it is though, but it solved all my issues

I have a css style called "loading"

.loading { display: none; }

The html for the loading div can be whatever but I used some FontAwesome icons and the spin method there:

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

On the elements that you want to hide you simply write this:

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

and in the function i just set this on load.

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

I am using SignalR so in the hubProxy.client.allLocks function (when its done going through the locks) I juts put

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

This also hides the {{someField}} when the page is loading since I am setting the loading class on load and AngularJS removes it afterwards.


Since the functionality of position:fixed changed recently, I had difficulty showing the gif loader above all elements, so I had to use angular's inbuilt jQuery.

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

Controller

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

})

Hope this helps :)


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>

Add this 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;
}

And just in your angular add:

$rootScope.loading = false; $rootScope.loading = true; -> when $http.get ends.


Simple way without interceptors or jQuery

This is a simple way to show a spinner that does not require a third-party library, intercepters, or jQuery.

In the controller, set and reset a 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 the HTML, use the flag:

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

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

The vm.starting flag is set true when the XHR starts and cleared when the XHR completes.


This works well for 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>

Angular:

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

While the promise returned from $http is pending, ng-show will evaluate it to be "truthy". This is automatically updated once the promise is resolved... which is exactly what we want.


Adding onto @Adam's answer,

Use ng-show as suggested, however, in your case you want the functionality to have multiple requests and await all of them before the loader is hidden.

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

And then in your controller:

$scope.pendingRequests++;

let url = '/whatever_Your_URL_Is'
$http.get(url)
  .then(function(response) {
      $scope.pendingRequests--;
})

If you're wrapping your api calls within a service/factory, then you can track the loading counter there (per answer and excellent simultaneous suggestion by @JMaylin), and reference the loading counter via a directive. Or any combination thereof.

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

USAGE

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

https://github.com/wongatech/angular-http-loader is a good project for this.

Example here http://wongatech.github.io/angular-http-loader/

The code below shows a template example/loader.tpl.html when a request is happening.

<div ng-http-loader template="example/loader.tpl.html"></div>

The following way will take note of all requests, and hide only once all requests are done:

_x000D_
_x000D_
app.factory('httpRequestInterceptor', function(LoadingService, requestCount) {_x000D_
    return {_x000D_
        request: function(config) {_x000D_
            if (!config.headers.disableLoading) {_x000D_
                requestCount.increase();_x000D_
                LoadingService.show();_x000D_
            }_x000D_
            return config;_x000D_
        }_x000D_
    };_x000D_
}).factory('httpResponseInterceptor', function(LoadingService, $timeout, error, $q, requestCount) {_x000D_
    function waitAndHide() {_x000D_
        $timeout(function() {_x000D_
            if (requestCount.get() === 0){_x000D_
                LoadingService.hide();_x000D_
            }_x000D_
            else{_x000D_
                waitAndHide();_x000D_
            }_x000D_
        }, 300);_x000D_
    }_x000D_
_x000D_
    return {_x000D_
        response: function(config) {_x000D_
            requestCount.descrease();_x000D_
            if (requestCount.get() === 0) {_x000D_
                waitAndHide();_x000D_
            }_x000D_
            return config;_x000D_
        },_x000D_
        responseError: function(config) {_x000D_
            requestCount.descrease();_x000D_
            if (requestCount.get() === 0) {_x000D_
                waitAndHide();_x000D_
            }_x000D_
            var deferred = $q.defer();_x000D_
            error.show(config.data, function() {_x000D_
                deferred.reject(config);_x000D_
            });_x000D_
            return deferred.promise;_x000D_
        }_x000D_
    };_x000D_
}).factory('requestCount', function() {_x000D_
    var count = 0;_x000D_
    return {_x000D_
        increase: function() {_x000D_
            count++;_x000D_
        },_x000D_
        descrease: function() {_x000D_
            if (count === 0) return;_x000D_
            count--;_x000D_
        },_x000D_
        get: function() {_x000D_
            return count;_x000D_
        }_x000D_
    };_x000D_
})
_x000D_
_x000D_
_x000D_


You can try something like this as well:

Create directive :

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

Then you add something like this to mainCtrl

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

And HTML can looks like this:

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

Here's a version using a directive and ng-hide.

This will show the loader during all calls via angular's $http service.

In the template:

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

directive:

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

by using the ng-hide class on the element, you can avoid jquery.


Customize: add an interceptor

If you create a loading-interceptor, you can show/hide the loader based on a condition.

directive:

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

interceptor:

  • for example: don't show spinner when response.background === true;
  • Intercept request and/or response to set $rootScope.$broadcast("loader_show"); or $rootScope.$broadcast("loader_hide");

more info on writing an interceptor


Just use ng-show and a boolean

No need to use a directive, no need to get complicated.

Here is the code to put next to submit button or wherever you want the spinner to be:

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

And then in your controller:

$scope.dataIsLoading = true

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

if you want to show loader for every http request call then you can use angular interceptor to manage http request calls ,

here is a sample code

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

If you are using ngResource, the $resolved attribute of an object is useful for loaders:

For a resource as follows:

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

You can link a loader to the $resolved attribute of the resource object:

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

create directive with this code:

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

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

All answers are or to complicated, or need to set some variables on every request which is very wrong practice if we know the DRY concept. Here simple interceptor example, I set mouse on wait when ajax starts and set it to auto when ajax ends.

$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 has to be added in application config app.config. I showed how to change mouse on loading state but in there it is possible to show/hide any loader content, or add, remove some css classes which are showing the loader.

Interceptor will run on every ajax call, so no need to create special boolean variables ( $scope.loading=true/false etc. ) on every http call.


Sharing my version of the great answer from @bulltorious, updated for newer angular builds (I used version 1.5.8 with this code), and also incorporated @JMaylin's idea of using a counter so as to be robust to multiple simultaneous requests, and the option to skip showing the animation for requests taking less than some minimum number of milliseconds:

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

Just discovered the angular-busy directive that shows a little loader depending on some async call.

For example, if you have to make a GET, reference the promise in your $scope,

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

and call it like so :

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

Here is the GitHub.

You can also install it using bower (don't forget to update your project dependencies):

bower install angular-busy --save

For page loads and modals, the easiest way is to use the ng-show directive and use one of the scope data variables. Something like:

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

Here, while someObject is undefined, the spinner will show. Once the service returns with data and someObject is populated, the spinner will return to its hidden state.


This really depends on your specific use case, but a simple way would follow a pattern like this:

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

And then react to it in your template:

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

This is the easiest way to add a spinner i guess:-

You can use ng-show with the div tag of any one of these beautiful spinners http://tobiasahlin.com/spinkit/ {{This is not my page}}

and then you can use this kind of logic

_x000D_
_x000D_
//ajax start_x000D_
    $scope.finderloader=true;_x000D_
    _x000D_
          $http({_x000D_
    method :"POST",_x000D_
    url : "your URL",_x000D_
  data: { //your data_x000D_
     _x000D_
     }_x000D_
  }).then(function mySucces(response) {_x000D_
    $scope.finderloader=false;_x000D_
      $scope.search=false;          _x000D_
    $scope.myData =response.data.records;_x000D_
  });_x000D_
     _x000D_
    //ajax end _x000D_
    
_x000D_
<div ng-show="finderloader" class=spinner></div>_x000D_
//add this in your HTML at right place
_x000D_
_x000D_
_x000D_


Another solution to show loading between different url changes is:

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

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

And then in the markup just toggle the spinner with ng-show="loading".

If you want to display it on ajax requests just add $scope.loading++ when the request starts and when it ends add $scope.loading--.


You can use angular interceptor to manage http request calls

  <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


Here is my implementation, as simple as a ng-show and a request counter.

It use a new service for all request to $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--;
        });
    }
} ]);

And then you can use your loader base on the number of current calls:

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