I have a service called $doggedHttp
, which exposes the same interface as $http
.
Now I want to create a $doggedResource
service which is the angular $resource
service on top of $doggedHttp
instead of $http
. In other word I want to inject $doggedHttp
as the $http
service.
Also, in my application I want to be able to create both $doggedResource
and $resource
. Thus I cannot simply override $http
with $doggedHttp
.
I thought dependency injection should make this scenario easy to solve. Am I wrong ?
Instead I had to go deep into the angular source code to finally came up with a quite ugly solution :
angular.module('doggedResource', ['ngResource', 'doggedHttp'])
.config(function() {
var ngResource = angular.module('ngResource'),
doggedResource = angular.module('doggedResource');
// replace the placeholder below with the $resource factory from ngResource
doggedResource._invokeQueue[1][2][1][2] = ngResource._invokeQueue[0][2][1][2];
})
.factory('$doggedResource', ['$doggedHttp', '$parse', null /* this is just a placeholder */]);
Is there a better solution ?
Remark that we cannot use $provide.decorator
to replace the injected $http
service.
To illustrate the problem, here are the relevant parts of angular-resource.js :
angular.module('ngResource', ['ng']).
factory('$resource', ['$http', '$parse', function($http, $parse) {
function ResourceFactory(url, paramDefaults, actions) {
}
return ResourceFactory;
}
Looking at the code above, the $provide.decorator
callback will be passed ResourceFactory as an argument. At that time the dependency $http
has already been resolved. And since ResourceFactory use $http
inside a closure we cannot change it.
.config(function($provide) {
$provide.decorator( '$resource', [ "$delegate", function( $delegate ) {
// here $delegate is the ResourceFactory which has
// already been linked to `$http` by a closure.
}
}
You should probably write all the logic that is there in $doggedHttp
in a decorator for $http
. Once you decorate $http
, everything should work fine
EDIT : Correction for condition.
.config(function($provide) {
$provide.decorator( '$http', [ "$delegate", function( $delegate ) {
// here $delegate is the $http function.
function $doggedHttp(config){
//write your storage logic here.
// route all the $http calls through $delegate at the end...
return $delegate(config);
}
//dont forget to create shortcut method overrides.
//createShortMethods('get', 'delete', 'head', 'jsonp');
//createShortMethodsWithData('post', 'put');
// This is the simplest solution to what you wish to do..
if( condition ) {
return $doggedHttp;
}
else {
return $delegate;
}
//finally return the $doggedHttp ( and not the $delegate )
}
}
Alternately, you can write all your storage logic in a request interceptor
- You can inject anything and everything in there as well, so storing your calls and re-requesting can also be done at that stage.
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