Reputation: 1964
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
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
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