5

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.
  }
}
6
  • So, your 'doggedHttp', it is it's own module? You did angular.module('doggedHttp',[])? You didn't just add a factory to the current module? Commented Oct 14, 2013 at 5:09
  • Just out of curiousity, why do you need to do this? If it's for mocking during unit-testing there are better ways of doing it. Commented Oct 14, 2013 at 6:59
  • @aaronfrost yes 'doggedHttp' is a module that define '$doggedHttp' Commented Oct 14, 2013 at 8:28
  • @ivarni It's not for mocking $http. $doggedHttp is the same as $http but it behaves differenty in case of error. It will automatically retry and store the request in localStorage if there is no internet connection. It will also replay the stored requests once the Internet connection is back. This behavior is desirable only on some resources, not all of them them. Commented Oct 14, 2013 at 8:28
  • 1
    @ivarni unfortunately you cannot use $provide.decorator here. Inside the decorator callback, you get the ResourceFactory which has already been linked to $http via a closure inside the ResourceFactory's Factory. I will edit my question to make this more clear. Commented Oct 14, 2013 at 12:08

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

2 Comments

If I do that, any call to the $http service will be intercepted. Which is not what I want. I want to be able to use the base $http and $doggedHttp in the same application.
Does a simple if condition in the $delegate not work for you?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.