I'd like to be able to set up an AngularJS http interceptor which will set $rootScope.loading
to true
or false
, depending on whether an AJAX request is currently ongoing.
So far I've put the following together:
angular.module('myApp')
.config(function ($httpProvider) {
$httpProvider.responseInterceptors.push('loadingInterceptor');
var loadingFunction = function (data, headersGetter) {
$rootScope.loading = true
return data;
};
$httpProvider.defaults.transformRequest.push(loadingFunction);
})
.factory('loadingInterceptor', function ($q, $window, $rootScope) {
return function (promise) {
return promise.then(function (response) {
$rootScope.loading = false;
return response;
}, function (response) {
$rootScope.loading = false;
return $q.reject(response);
});
};
});
But I'm unable to inject $rootScope
into the config block, so I have no way of setting the $rootScope.loading
variable when an HTTP request begins.
Am I missing something here? How should I do this?
responseInterceptors
has been deprecated http://docs.angularjs.org/api/ng.$http. I have enhanced the previous example:
app.factory('myHttpInterceptor', ['$q', '$rootScope', '$injector',
function ($q, $rootScope, $injector) {
$rootScope.showSpinner = false;
$rootScope.http = null;
return {
'request': function (config) {
$rootScope.showSpinner = true;
return config || $q.when(config);
},
'requestError': function (rejection) {
$rootScope.http = $rootScope.http || $injector.get('$http');
if ($rootScope.http.pendingRequests.length < 1) {
$rootScope.showSpinner = false;
}
if (canRecover(rejection)) {
return responseOrNewPromise
}
return $q.reject(rejection);
},
'response': function (response) {
$rootScope.http = $rootScope.http || $injector.get('$http');
if ($rootScope.http.pendingRequests.length < 1) {
$rootScope.showSpinner = false;
}
return response || $q.when(response);
},
'responseError': function (rejection) {
$rootScope.http = $rootScope.http || $injector.get('$http');
if ($rootScope.http.pendingRequests.length < 1) {
$rootScope.showSpinner = false;
}
if (canRecover(rejection)) {
return responseOrNewPromise
}
return $q.reject(rejection);
}
}
}
]);
And you can use the factory like so:
app.config(function ($httpProvider) {
$httpProvider.interceptors.push('myHttpInterceptor');
});
You can only insert providers in module.config, but you shouldn't do this in the default transformers anyway, you should do it in the interceptors (like you do when you're setting loading = false).
There might be more than one request going on at the same time. Something like this might work:
angular.module('myApp')
.config(function ($httpProvider) {
$httpProvider.responseInterceptors.push(function ($rootScope) {
$rootScope.numLoadings = 0;
$rootScope.loading = false;
return function (promise) {
$rootScope.numLoadings++;
$rootScope.loading = true;
// make sure the loading screen is visible
var hide = function (r) {
if ((--$rootScope.numLoadings)===0){
//console.log('hide the loading screen');
$rootScope.loading = false;
}
return r;
};
return promise.then(hide, hide);
};
});
});
Ofcourse you don't have to put numLoadings in the root scope, I just put it there for this example: http://plnkr.co/edit/32Mh9UOS3Z4vnOtrH9aR?p=preview
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With