11

By default, the $resource.query() is set up to expect an array of objects that become $resource objects. To accommodate paging in a nice, restful way, I have my GET /api/widgets endpoint set up to return the following object:

{
  currentPage: 1,
  perPage: 20,
  totalItems: 10039,
  items: [{...}, {...}, {...}]
}

Is there a way to make it so that angular will know that the items property is the array of items to be $resource objects?

w.brian
  • 16,296
  • 14
  • 69
  • 118

4 Answers4

23

You need to specify your own custom action.

I imagine your code looks something like this:

factory('Widget', function($resource) {
  return $resource('/api/widgets');
});

Change it to this:

factory('Widget', function($resource) {
  return $resource(/api/widgets, null, {
    query: {
      method: 'GET',
      isArray: true,
      transformResponse: function(data) {
        return angular.fromJson(data).items;
      }
    }
  });
});
w.brian
  • 16,296
  • 14
  • 69
  • 118
Remco Haszing
  • 7,178
  • 4
  • 40
  • 83
  • So, with this approach, how do I get access to the paging information from outside the transformResponse method? You've definitely already answered the original question, but now I have a new problem :) – w.brian Jan 26 '15 at 19:22
  • Not entirely sure, but you could try this `var widgets = Widget.query(); widgets.$promise.then(function(data) {console.log(data);});`. – Remco Haszing Jan 26 '15 at 19:33
  • 5
    So what I did, was set `isArray: false` and keep the shape of the returned object. I iterated through `item in items` and just replaced them: `data.items[i] = new Widget(item);` It works great! I really appreciate your help with this. – w.brian Jan 26 '15 at 19:45
2

the easy was is to use $resouce.get, if you wan to use query you can override that behaivor.

$resource('/notes/:id', null,
{
    'query':  {method:'GET', isArray:false}
});

more info https://docs.angularjs.org/api/ngResource/service/$resource

bto.rdz
  • 6,636
  • 4
  • 35
  • 52
  • This doesn't solve my problem. The entire response object will be a `$resource`, and attempting to call `$save` or `$update` will attempt to persist values that aren't part of the the actual resource's model. I need to configure `$resource.query()` to know that the actual resource objects are contained in the `items` array of the response. – w.brian Jan 26 '15 at 18:25
  • I have done this, and works for me, I dont see how it wont work, when you call the update you will speciy the id and the new object, i dont see how it wont work `myresource.update({id:1}. { propery1: 1, property2: 2 })` – bto.rdz Jan 26 '15 at 18:35
  • This will work if I'm using `$resource` strictly as a mechanism for constructing requests and getting a response object, however, there are additional features of `$resource` that aren't widely understood. Specifically, the object(s) returned by a REST endpoints become `$resource` objects themselves. In my case, I needed a way to tell angular that the `list` property of the returned object are the actual resources. See the accepted answer. – w.brian Jan 26 '15 at 19:15
0

I just had the same problem, and I wanted to propose a solution which could be a little better:

factory('Widget', function($resource) {
    return $resource(/api/widgets, null, {
        query: {
            interceptor: {
                response: function(response) {
                    return response.data.items;
                }
            }
        }
    }
}

I think it may be better, because you are reusing the standard angular behaviour (which is, actually, doing a little more than fromJson) and intercepting the output result to filter what you want.

Edouard Berthe
  • 1,393
  • 2
  • 18
  • 38
0

I use this pattern for query with paging informations.

module.config(function($resourceProvider){
    $resourceProvider.defaults.actions.query = {
        method: 'GET',
        interceptor: {
            response: function(response) {
                response.resource.$totalCount = response.data.totalCount;
                response.resource.$limit = response.data.limit;
                response.resource.$offset = response.data.offset;
                return response.resource;
            }
        },
        transformResponse: function(data, headers, status) {
            var out = angular.fromJson(data);
            out.data.totalCount = out.totalCount;
            out.data.limit = out.limit;
            out.data.offset = out.offset;
            return out.data;
        },
        isArray: true
    };
})