Although I’m sure there will be a more conventional approach in the future, I wanted to share my approach and hopefully get some feedback.
I sketched out several possibilities, all of which involve a custom ArtistAdapter
, and some of which involved changes to the model
hook in ArtistsPopularRoute
:
- override
buildURL
and find
methods and have total control/responsibility over the entire flow. This kept the current syntax
@store.find('artist', 'popular')
but means I need to distinguish between the cases where I’m attempting to find a single resource (the artist resource at /artists/1
) and grabbing a subset (get me the popular artists at /artists/popular
). That implementation looked something like:
ArtistAdapter = DS.RESTAdapter.extend
subsets: ['popular', 'recent', 'following']
...
find: (store, type, id, record) =>
if @get('subsets').contains(id)
@findMany(store, type, id, record)
else
@_super(store, type, id, record)
buildURL: (type, id, record) ->
# keeping this simple...
if id == 'popular' then '/api/artists/popular'
...
I had expected a call to findMany
to simply work, as it should be using buildURL
under the hood to resolve the URL (which it does), and then push the returned artists array onto the store.
Unfortunately, this doesn’t work, as Ember Data sets the requestType
on the serializer based on the initial call (in our case find
), which causes an exception to be raised when pushing to the store, as the store is expecting only a single Artist
instead of the array we’re returning.
- call
find
, passing a pseudo model like this:
@store.find('popular')
At least in this case the store would expect an array back, and the real type could be surmised based on the route, but this would require overwriting find
again, with a more complex version of buildURL
. In addition, it is extremely surprising behavior if you’re familiar with the different ways of calling find
, so I rejected this almost immediately.
- pass the subset as a query argument to
find
and define a custom findQuery
Although it is a private method, it has all the characteristics I cared about: no need to define custom serializer behavior, straightforward version of buildURL
, and it seems flexible in case I add additional arguments such as pagination. Most importantly, it just works.
So the model hook now looks like this:
ArtistPopularRoute = Ember.Route.extend(...mixins...,
model: ->
@store.find('artist', { subset: 'popular'})
)
And the corresponding ArtistAdapter
:
ArtistAdapter = DS.RESTAdapter.extend(
subsets: Em.A(['popular', 'recent', 'following'])
namespace: 'api'
findQuery: (store, type, query) ->
subset = query.subset
delete query.subset
@ajax(@buildURL(type.typeKey, subset), 'GET', { data: query })
buildURL: (type, id, record) ->
namespace = Ember.get(this, 'namespace')
subsets = Ember.get(this, 'subsets')
if subsets.contains(id)
"#{namespace}/artists/#{id}"
else
@_super(type, id, record)
)
With this in place, I’m at least able to keep moving forward. I plan on extracting this behavior to a Mixin so I can simply define a subsets
property on each route that relies on this behavior. While I’m happy that this appears to work, it’s probably not ideal.
If anyone has any feedback, I’d be happy to hear it.