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