40

I've found an example of a loading spinner for http/resource calls here on SO:

As you can see the implementation works (using AngularJS 1.0.5). However if you change the sources to AngularJS 1.1.5. The example does not work anymore.

I learned that $httpProvider.responseInterceptors is deprecated in 1.1.5. Instead one should use $httpProvider.interceptors

Unfortunately just replacing the above string in the Plunker did not solve the problem. Has anyone ever done such a loading spinner using HttpInterceptor in AngularJS 1.1.5?

Thanks for your help!

Michael

2
  • The plunk works for me. What doesn't work for you? What result are you expecting? Commented Jul 24, 2013 at 17:02
  • 1
    Instead on manually intercepting/counting request, you can just watch for $http.pendingRequests.length > 0 Commented Jul 24, 2014 at 15:32

3 Answers 3

112

Thanks to Steve's hint I was able to implement the loader:

Interceptor:

.factory('httpInterceptor', function ($q, $rootScope, $log) {

    var numLoadings = 0;

    return {
        request: function (config) {

            numLoadings++;

            // Show loader
            $rootScope.$broadcast("loader_show");
            return config || $q.when(config)

        },
        response: function (response) {

            if ((--numLoadings) === 0) {
                // Hide loader
                $rootScope.$broadcast("loader_hide");
            }

            return response || $q.when(response);

        },
        responseError: function (response) {

            if (!(--numLoadings)) {
                // Hide loader
                $rootScope.$broadcast("loader_hide");
            }

            return $q.reject(response);
        }
    };
})
.config(function ($httpProvider) {
    $httpProvider.interceptors.push('httpInterceptor');
});

Directive:

.directive("loader", function ($rootScope) {
    return function ($scope, element, attrs) {
        $scope.$on("loader_show", function () {
            return element.show();
        });
        return $scope.$on("loader_hide", function () {
            return element.hide();
        });
    };
}
)

CSS:

#loaderDiv {
   position: fixed;
   top: 0;
   right: 0;
   bottom: 0;
   left: 0;
   z-index: 1100;
   background-color: white;
   opacity: .6;
}

.ajax-loader {
   position: absolute;
   left: 50%;
   top: 50%;
   margin-left: -32px; /* -1 * image width / 2 */
   margin-top: -32px; /* -1 * image height / 2 */
   display: block;
}

HTML:

<div id="loaderDiv" loader>
    <img src="src/assets/img/ajax_loader.gif" class="ajax-loader"/>
</div>
Sign up to request clarification or add additional context in comments.

10 Comments

@Bixi mentioned, replace counters with code:- var _http = null; (top of return), inside response and responseError, _http = _http || $injector.get('$http') and check for _http.pendingRequests.length < 1
what if we have 2 http requests which start the loading at same time but end it at different time like codepen.io/anon/pen/emZWON?editors=101
Just need some clarification - why if (!(--numLoadings)) { inside the responseError function and not if ((--numLoadings) === 0) { as in the response function? Sorry, not really sure what !(--numLoadings)) mean? (its treating a number as if its a boolean?)
This solution can not distinguish between areas. Example loading different panels from server would show all loading until last request has got its response. BUT I will use this idea to disable all buttons during post/put/delete request
This is great...EXCEPT...it is jQuery dependent: element.show/.hide are not supported in angular's JQLite.
|
18

"responseInterceptors" was deprecated. "interceptors" replaced it with many enhancements in a preview version. Off the top of my head I don't remember which version. Documentation on this is sparse, so you're probably best off examining the source code.

The gist of the change looks like this:

$httpProvider.interceptors.push(function($q, $rootScope) {
  return {
     'request': function(config) {
        // intercepts the request
     },
     'response': function(response) {
       // intercepts the response. you can examine things like status codes
     },
     'responseError': function(response) {
       // intercepts the response when the response was an error
     }
  }
});

In the angular source you will find documentation under "* # Interceptors" in the $HttpProvider function. There is an example usage very similar to what I posted above.

1 Comment

Thanks for the hint Steve!
2

The provided/accepted solution is fine IF you want to include JQuery in your solution, which the AngularJS team is recommending against going forward. element.show/.hide are not supported in Angular's JQLite.... So the following refactors are necessary to run in an non-jquery session:

Change the HTML element to add a class of 'hidden'

<div id="loaderDiv" loader class="hidden">
     <img src="Content/images/yourgif.gif" class="ajax-loader" />
</div>

Add the hidden class to your css:

.hidden{display:none !important}

And tweak the directive thus:

(function() {
    'use strict';

    angular
        .module('your_app')
        .directive('yourSpinner', yourSpinner);

    yourSpinner.$inject = ['$rootScope'];

    function yourSpinner($rootScope) {
       return function($scope, element, attrs) {
           $scope.$on("loader_show", function () {
               if (element.hasClass("hidden")) {
                   element.removeClass("hidden")
               }
            });
            return $scope.$on("loader_hide", function () {
                if(!element.hasClass("hidden")){
                    element.addClass("hidden")
                }
            });
        }
    }

})();

The factory is fine as-is.

1 Comment

I believe in some time this answer gets to the first place with votes.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.