18

I'm using an API that returns JSON data in this format:

{
    paging: {
        previous: null,
        next: null
},
    data: [
        { title: 'First Item' },
        { title: 'Second Item' },
        ...
    ]
}

I'm using Angular's $resource service to fetch this data.
My code - which is located in a controller - goes something like this:

var Entity = $resource('/api/entities');
var entities = $scope.entities = Entity.get();

And then, in the view, I can display the data like this:

<ul>
  <li ng-repeat="entity in entities.data">{{entity.title}}</<li>
</ul>

It all works fine, but:

  • I'd rather expose only the contents of entities.data to the view, instead of the whole entities object. How can I intercept the data returned by the GET request to modify it before it populates $scope.entities?
  • Correlated question: since I am fetching an array of data, it would be cleaner to use Entity.query() instead of Entity.get(). But if I use Entity.query() in the code above, I get an error "TypeError: Object # has no method 'push'". This makes sense, since the API is returning an object instead of an array (hence, no 'push' method on the object). Again, if I could extract the .data attribute from the response, I'd have an array.

Following these indications by Dan Boyon, I managed to customize the default $resource service and to override the .get() or .query() methods, but I'm not sure where to go from there.

Community
  • 1
  • 1
AngularChef
  • 13,797
  • 8
  • 53
  • 69

2 Answers2

24

I don't think you need to modify the get or query defaults. Just use the success callback to do what you want. It should be more robust as well.

Entity.get(
    {}, //params
    function (data) {   //success
        $scope.entities = data.data;
    },
    function (data) {   //failure
        //error handling goes here
    });

Html will be cleaner, too:

 <ul>
      <li ng-repeat="entity in entities">{{entity.title}}</<li>
 </ul>

By the way, I usually declare services for my resources and then inject them into my controllers as I need them.

 myServices.factory('Entity', ['$resource', function ($resource) {
     return $resource('/api/entities', {}, {
     });
 }]);
Chris
  • 3,925
  • 2
  • 18
  • 15
  • Hey Chris! Thanks, that works perfectly. I was aware of the success and failure callbacks, but it didn't occur to me to do the scope assignment there. – AngularChef Aug 08 '12 at 16:56
  • Trying this same method, I'm getting an error saying "TypeError: Object # has no method 'push'". The documentation for $resource is really bad. Their examples look totally different from yours and neither is working. – Ben Lesh Aug 09 '12 at 00:41
  • 3
    @blesh that error usually occurs when you're receiving back an array into $resource when isArray: false, such as using get. If you're receiving an array you probably want to use query which defaults isArray: true. – Chris Aug 09 '12 at 01:38
  • That was exactly the problem. Now however, the array of strings I'm getting back from my service actually comes back as an array of arrays. So rather than it being: ["one", "two"] it's [['o','n','e'],['t','w','o']] ... very strange. – Ben Lesh Aug 09 '12 at 02:26
  • 2
    ... apparently my issue mentioned in the above comment has to do with me returning an array of strings and not objects. Which makes sense given how $resource adds methods to the results it gets. https://groups.google.com/forum/#!msg/angular/QjhN9-UeBVM/UjSgc5CNDqMJ%5B1-25%5D – Ben Lesh Aug 09 '12 at 02:30
  • this solution works fine for query, but you can no longer call data.$save() to get the restful behavior of the $resource service. I'm still looking for a workaround to that. – JBCP Jul 30 '13 at 23:17
1

You can use the Response Transformer (transformResponse) like this:

$resource('/api/entities', {}, {
        query: {
            method: 'GET',
            responseType: 'json',
            isArray: true,
            transformResponse: function (response) {
                return response.data;
            }
        }
    });

This code modifies the "query" method behaviour, you can do the same for "get" ... !

Ismail RBOUH
  • 10,292
  • 2
  • 24
  • 36