AngularJS’s built-in ngResource is a great tool for natively supporting REST APIs in your Angular application. But what happens when you need to support something besides a simple call that retrieves a list of JSON objects? You quickly run into the limits of ngResource.
Here’s a great case where you might need to do something more complex: paging. Say you want to get a list of objects, and there’s 10,000 or so of them. You don’t want to send 10,000 objects to your frontend app. You want to send a portion of them, but you still need to indicate to the app that there are more.
Surprisingly, considering how widespread this pattern is in web development, there does not seem to be a native way to accomplish this. But you can extend ngResource. Here’s how I did it.
From The Backend
There is some debate on what the proper way to handle paging is for RESTful endpoints. Some people say you should send it as a header. Other say you should send it as part of the data return. I chose the latter. Here’s an example of a return I might implement:
As you can see here, the data is returned in a wrapper structure that indicates how many matches were found and and how many are returned and that we’re only returning a subset. So the frontend knows that it can page the results.
Intercepting The Response
The next thing I did was implement an $httpProvider
transformer to transform
the response.
What this does is pull out the values and add them (temporarily) to the data array before passing that on ngResource. The try/catch block is to check to be sure we actually got a valid JSON response and not, say, a template or something. And if it’s not a JSON structure in a format we recognize (like an endpoint that hasn’t been converted to the new structure) it just passes the full data along.
The nice thing about doing this with an transformer at the $http
level is that
this logic now lives here instead of in your models.
Extending ngResource
So now you have the $found
and $returned
being passed along to ngResource,
but ngResource is still discarding it. Here’s where the magic happens. We create
a wrapper provider around ngResource that can pull these variables out and make
them available.
Basically what this does is extend $resource
to add a interceptor for each
action that pulls the variables from the data collection and puts them on the
resource itself.
With this, you now have $found
and $returned
as properties on the resource
collection returned. Instead of calling $resource(...)
in your factories, you
now call $ourResource(...)
instead. It’s a 1:1 replacement since
$ourResource
wraps and extends $resource
.
Now, with that done, you can do something like this in your views:
Theoretically you could even implement the entire thing in your $ourResource
wrapper by implementing transformResponse
in the inner angular.forEach
loop.
That might even be a better idea, but I like this approach.
Thanks goes to this JSFiddle that put me on the right path to solving this.