Intro
A very common pattern in web applications, be them server- or client side, is
resource URLs. We might have a list of users that we want to show at /users
and
then different pages related to the user which is encoded in the URL. These
might be e.g /users/dave-hopkins/activity
and /users/dave-hopkins/followers
.
The pattern is a top-level URL to list all the resource instances, and then separate pages to display pieces of information regarding specific resource instances.
Artists and songs
That’s exactly what I did for the the Rock & Roll application, where the routes were defined as such:
1 2 3 4 5 |
|
The simplest thing that works. However, the above is not ideal especially when
more pages (or views, if you will) are added below the artists
resource route.
That’s because the singular artist instance is encoded in the songs
route, by
having its identifier (in this case, slug) in the path of that route.
Imagine we need to add additional info about each band. Just blindly extending the above URL scheme, this would become:
1 2 3 4 5 6 |
|
The cracks start to show. The artist for both the artists.songs
and the
artists.info
routes would have to be fetched in both routes, with identical
code. Nested routes -and how it lends itself to a nested UI- is truly
a masterpiece, a shining emerarld on Ember’s crown. It would be a pity not to
take advantage of it.
DRY up those routes
So we established that the problem is having the artist “encoded” in all routes
below the top-level artists
resource. The solution is consequently pretty
straightforward -this always seems to be the case in retrospective-, let’s
extract the path segment that represents the artist:
1 2 3 4 5 6 7 |
|
With the introduction of the artist
resource, the duplication is gone, but we
are not done yet. First, we have to define the route and set up its model hook.
Second, since the “routing table” has changed, we’ll have to adjust route names
and code that uses them. Since the naming conventions in Ember have the route
names as their basis, we’ll probably have to change code in several places.
Route changes
Resource routes reset the routing namespace, so the route that corresponds to
the artist
route name in the table is App.ArtistRoute:
1 2 3 4 5 6 7 8 9 10 11 |
|
That is exactly what we had for App.ArtistsSongsRoute
in the previous version,
which makes sense. The artist is now fetched one route level higher.
For simple, non-resource routes, the name of the route is the name of the
resource route above (if it exists) plus the name of the route itself. In this
case, the route name is artist.songs
which gets resolved as
App.ArtistSongsRoute
):
1 2 3 4 5 6 7 8 9 10 11 |
|
The first interesting thing is modelFor
. It gets the model for another,
already resolved route. In Ember route models are resolved stepping down
from the top-level application route. That means that at this point we can be
certain that the artist
route already has its model, the artist instance
resolved.
The model of this route is simply the songs belonging to that artist.
The other interesting bit is setupController
. We have already come across this
hook before; it is the place to do additional
setup -above fetching the model and deciding which template to render- for the
controller. Since we’ll want to display artist-related data in the template, we
store it in an artist
property and we make sure to call _super
, the
implementation of this hook in Ember.Route
, that sets the controller’s model
property to the model argument in this method.
Templates & controllers
The mechanical part of the routing update is to replace all occurrences of
the artists.songs
route name to artist.songs
.
What deserves more attention is that the controller for artist.songs
now has
the songs of the artist as its model, not the artist itself. That means that we
should adjust the controller type it extends:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
All changes are made necessary by the model change. Properties of the artist now
need to be prefixed by artist
(e.g name
=> artist.name
) while properties of the
songs no longer need to have the songs
prefix since it is the model (e.g
songs.length
=> length
).
This also holds true of the template. To give an example, rendering the stars for each song can becomes more concise:
<script type="text/x-handlebars" data-template-name="artist/songs">
(...)
{{#each}}
<div class="list-group-item">
{{title}}
{{star-rating item=this rating=rating maxRating=5 setAction="setRating"}}
</div>
{{else}}
(...)
</script>
The #each helper, without parameters, loops through the items in the model of the template, in our case, the songs, which is exactly what we want.
That wraps up our route sanitizaion. In the next post, we will take advantage of
the benefit that the songs
route now has the artist’s songs as its model.