Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

Issue with interceptors in $resource #11201

Closed
Avcajaraville opened this issue Feb 27, 2015 · 11 comments
Closed

Issue with interceptors in $resource #11201

Avcajaraville opened this issue Feb 27, 2015 · 11 comments

Comments

@Avcajaraville
Copy link

Im having a weird behavior manipulating a response through $resource with a custom interceptor.

If I use a response interceptor like this :

angular.module( 'app.services', [] )
  .factory( 'Product', function( $resource, routesConfig ) {
      return $resource( routesConfig.catalogueEndPointg(), {}, { 
        query: { 
          method :'GET', 
          isArray : false, 
          params : { 
            page : '@currentPage'
          },
          interceptor: {
            response: function( response ) {
              // DO STUFF
              return response;
            }
          },
        } 
      });
    });

Then, from my controller :

angular.module( 'app.controllers', [])
  .controller( 'DummyController', function( $scope, Product ){
    productsPromise.$promise
      .then(
        // Success
        function ( response ) {
          console.log( response );
        });
  });

At this point, this is the output of the console.log( response ) :

captura de pantalla 2015-02-26 a la s 17 40 11

On the response object, I have the data object, as expected, but I also have this resource : Resource object which also includes the response data again.

But, if I dont use a interceptor at all; Im getting the expected response :

captura de pantalla 2015-02-26 a la s 17 42 47

I dont understand this behavior and Im concern about perform or memory issues.

Can somebody clarify this ?

PS: I need to use interceptors, because I have to modify the response from server.

Original question on stackoverflow:

http://stackoverflow.com/questions/28748053/angularjs-issue-with-interceptors-in-resource/

@pkozlowski-opensource
Copy link
Member

@Avcajaraville hard to say without a reproduce scenario... Could you put minimal code into plunker or similar. We are missing essential info here: AngularJS version used, exact interceptor code etc.

@Avcajaraville
Copy link
Author

Im using angular 1.3.11.

The interceptor code is not very important I think, but here it is :

interceptor: {
  response: function( response ) {
    var responseSize = response.data.data.length;
    for ( var i = 0; i < responseSize; i++ ) {
      response.data.data[ i ].image = 'hardoced-url';
    }
    return response.resource.data;
  }
},

We need to add this hardcoded-url to every object on the response (debugging purpose while our team finish the api).

As pointed on the stackoverflow answer, seems angular makes this on purpose.

Also, comparing both objects when using interceptors (this way):

console.log( response.data.data == response.response.data );

Is returning true, so I guess they have the same reference, thus, not causing memory issues.

But still, its a weird behavior. I want to know why this happen and, if its expected at all or not.

@gkalpak
Copy link
Member

gkalpak commented Feb 27, 2015

ngResource has a default response interceptor that returns response.resource:

function defaultResponseInterceptor(response) {
  return response.resource;
}

This default interceptor is used only if there is no response interceptor specified by the user.
If you want to add your interceptor, you too need to manipulate and return response.resource (instead of response).

$resource('...', {...}, {
  someMethod: {
    ...
    interceptor: {
      response: function (response) {
        var res = response.resource;
        // Do stuff with `res`
        return res;
      }
    }
  }
}); 

(Demo fiddle)

@pkozlowski-opensource
Copy link
Member

@Avcajaraville I hope that @gkalpak answer covers your pb.

@Avcajaraville
Copy link
Author

@pkozlowski-opensource @gkalpak

Ok, thanks for the answer then ! It really helps, the only thing I still missing is a brief answer about the "why", but know that is an expected behavior of angular makes me feel more comfortable about it.

@gkalpak
Copy link
Member

gkalpak commented Mar 2, 2015

@Avcajaraville, I will gladly explain the "why" if I understand the question :)
Why what ?

@Avcajaraville
Copy link
Author

@gkalpak Why when using a custom interceptor, returning response will automatically add a resource with resoruce.data property ? We already got it on response.data.

@gkalpak
Copy link
Member

gkalpak commented Mar 2, 2015

The default interceptor won't add any property (the property is already there, added by ngResource for reasons I'll explain shortly). All the interceptor does, is return response.resource instead of response or response.data.

How ngResource "works":

Consider the following code:

var User = $resource(...);
var userInstance = User.get({id: 1});
// Request still pending | `userInstance` is an instance of `User`
// ...
// Response arrived | `userInstance` updated with new fields (from `response.data`)

Basically, when you create a resource instance, User.get() (or whatever method you use) returns an object (or an array if isArray: true), which is an instance of User.
$resource keeps a reference of this returned userInstance, so it can populate it with more properties as soon as the response arrives (basically, it will be extending userInstance with response.data).

Why does ngResource do this

This is a very handy feature of ngResource that allows you to avoid some boilerplate, when retrieving data that need to be displayed in views. Compare the following snippets:

<!-- In the view -->
Current user: {{ user.username }} &lt;{{ user.email }}&gt;
// Now, you can do this:
$scope.user = User.get({id: 1});

// You DON'T need to do this:
User.get({id: 1}).$promise.then(function (user) {
  $scope.user = user;
});

Why not directly return resource

So, why rely on a default interceptor for returning response.resource ? Why not have $resource directly return resource in the first place (instead of attaching it to response) ?
This is for allowing you to use custom interceptors that do have access to other response-related data (such as headers, status etc), while still only returning resource if you choose not to use a custom interceptor.


I hope that clears things up. Let me know if it doesn't :)

@Avcajaraville
Copy link
Author

@gkalpak Perfect answer ! Thanks so much !

BTW; I have answer my own question on SO and credit you -> http://stackoverflow.com/questions/28748053/angularjs-issue-with-interceptors-in-resource/28812722#28812722

@jbmikk
Copy link

jbmikk commented Mar 16, 2016

I don't understand how is it that this default interceptor is no documented anywhere, not in the $http documentation and not in the $resource documentation. Is it documented elsewhere? Am I missing something?

@gkalpak
Copy link
Member

gkalpak commented Mar 19, 2016

ngResource's interceptor concept is a bit weird. This is on our radar though and hopefully the situation will improve soon.

(BTW, there's been some discussion on #9334 (comment) and linked issues.)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants