Sayak Banerjee
Sayak Banerjee

Reputation: 1964

AngularJS: plug spinner into $httpProvider

I have a spinner defined in my ng-app's base template. And I have the following code to show/hide it automatically whenever there is an active AJAX request on the way:

app.config(function ($httpProvider) {
    // Request interceptor(s)
    $httpProvider.defaults.transformRequest.push(function (data, headersGetter) {
        window.httpStart();
        return data;
    });

    // Response interceptor(s)
    $httpProvider.responseInterceptors.push('spinnerInterceptor');
})

app.factory('spinnerInterceptor', function ($q, $window) {
    return function (promise) {
        // Hide spinner on success.
        return promise.then(function (response) {
            window.httpEnd();
            return response;
        },

        // Hide spinner on failure.
        function (response) {
            window.httpEnd();
            return $q.reject(response);
        });
    };
});

app.config(function () {
    setInterval(function () {
        if (typeof (window.httpQueue) != 'undefined') {
            if (window.httpQueue > 0) {
                angular.element('#ng-loading').show();
            } else {
                angular.element('#ng-loading').hide();
            }
        }
    }, 50);

    /**
     * Mark the start of a new HTTP request.
     */
    window.httpStart = function httpStart() {
        if (typeof (window.httpQueue) == 'undefined') {
            window.httpQueue = 0;
        };

        window.httpQueue++;
    };

    /**
     * Mark the end of a HTTP request.
     */
    window.httpEnd = function httpEnd() {
        if (typeof (window.httpQueue) != 'undefined') {
            window.httpQueue--;
        };
    };
});

I hate using setInterval here in view of performance, specially because our (corporate) userbase is going to use this on IE8.

Is my concern unfounded? If not, how can I improve this piece of code.

Upvotes: 0

Views: 286

Answers (2)

user2943490
user2943490

Reputation: 6940

There's no reason to put your helper httpStart and httpEnd methods on the global window object. Since an interceptor is defined using .factory(), it can treated like any other angular factory that has its own functions, with one exception: the recommended method for defining interceptors is to return an object with specific properties (response/request/requestError/responseError).

Interceptor/factory:

app.factory('spinnerInterceptor', function($q, $rootScope) {
    // this gets set once, when the interceptor is first requested.
    $rootScope.httpQueue = 0;

    return {
        'request': function(config) {
            $rootScope.httpQueue++;
            return config;
        },
        'response': function(response) {
            $rootScope.httpQueue--;
            return response;
        }
    };
});

HTML:

<div id="ng-loading" ng-show="$root.httpQueue > 0"></div>

Upvotes: 1

kwangsa
kwangsa

Reputation: 1711

You can also use store if the ng-loading need to be shown or not in the $rootscope and afterward just using normal angularjs html

angular.module('myApp', []).run(function($rootScope) {
     $rootScope.httpQueue = 0;
})

app.config(function ($rootscope) {
window.httpStart = function httpStart() {
    if (typeof ($rootScope.httpQueue) == 'undefined') {
        $rootscope.httpQueue = 0;
    };

    $rootScope.httpQueue++;
};

/**
 * Mark the end of a HTTP request.
 */
window.httpEnd = function httpEnd() {
    if (typeof ($rootScope.httpQueue) != 'undefined') {
        $rootScope.httpQueue--;
    };
};
});

HTML

<div id="ng-loading" ng-show="$root.httpQueue > 0"/>

If you can avoid to use global variable then use controller / service as better approach.

Upvotes: 1

Related Questions