What options are there there for resolving nested resources in ngResource responses?
There have been some related questions about resolving endpoints for nested resource in ngResource, but this question is about when a REST response contains a second resource nested in the collection that is being queried, especially 1-to-1 mappings where you wouldn't have e.g. pets/<id>/owner
as its own resource.
Say there are two resources, Pets
and Owners
:
GET /pets:
[{
name: 'spark',
type: 'dog',
owner: '/owners/3/' # alternatively just '3' or the full object.
}]
As a developer, I sometimes want to query the Owner
resource as a whole, sometimes I want to query the Pet
resource and then I automatically want to resolve the owner
attribute into a resource instance.
This is my current solution:
.factory('Pet', function ($resource, Owner) {
var Pet = $resource('/pets/:id', {id: '@id'});
Pet.prototype.getOwner = function () {
return new Owner(this.owner); // or Owner.get({id: this.owner})
}
return Pet;
})
Problems here are many. There's integrity – for one. This implementation, I believe, allows for multiple instances of the same resource. Then there's practicality. You also have additional attributes to keep track of (owner
and getOwner()
, instead of just owner
; possibly setOwner
if you want to be able to save the model).
An alternative solution could be built on transformResponse
, but it would feel like a hack to include that in every resource that has a nested mapping.
I believe this is the exact reason why Martin Gontovnikas created Restangular. He didn't like having to deal with nested $resources in the main angular framework. I think his Restangular solution would fit nicely into your needs. His code is on GitHub here and he's got a nice intro video on youtube here.
Check it out. I think you'll find it does exactly what you want it to do.
Update: I ended up working on this for a bit and have started a new angular module, available on GitHub. The answer below is about the Gist I wrote originally.
There doesn't seem to be anything around there like what I have been looking for. I have started an implementation of a solution that only supports get
and getList
(query) operations. The remaining methods should be trivial to add since I've pretty much kept with the layout of the ngResource module. The Gist for my implementation is below.
https://gist.github.com/lyschoening/7102262
Resources can be embedded in JSON either as full objects that simply get wrapped in the correct Resource
model, or as URIs, which get resolved automatically. In addition to embedded resources, the module also supports typical nested resources, either as true parent-child collections (where the resource is only accessible after selecting the parent) or as cross-referenced collection.
Yard = Resource('/yard') # resource model
Yard.$nested('trees') # embedded item or list of items
Chair = Resource('/chair')
Yard.$nested('/chair') # sub-collection without its own model
# (for many-to-many)
Tree = Resource('/tree')
# child-collection with its own model
TreeHouse = Tree.$childResource('/treehouse')
yard = Yard.get(1)
# GET /yard/1
# {
# "uri": "/yard/1",
# "trees": [
# "/tree/15", -- reference, looked-up automatically with GET
# {"uri": "/tree/16", "name": "Apple tree"}
# -- full object, resolved to Tree instance
# ]
# }
# GET /tree/16
# {"uri": "/tree/15", "name": "Pine tree"}
yard.chair.getList()
# GET /yard/1/chair
# [{"uri": "/chair/1", ...}, ..]
# -- model inferred from URI
yard.trees[0].treehouse.getList()
# GET /tree/15/treehouse
# [{"uri": "/tree/15/treehouse/1", ...}, ..]
# -- automatically resolved to TreeHouse instance
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With