Problem
The default behavior of Ember's link-to
helper and Ember.Route
's
model hook make it easy to identify and load records by id. However,
you may want to use a human-readable keyword or slug to identify
a record in a url in place of an id.
Solution
Ember makes it easy to override the Route's default
model
hook. You can define a custom model function to look up records by a
slug
property instead of by the id
.
Changing the link-to
to use a slug instead of an id
is easy and
only requires creating a custom
serialize
function on the route.
Discussion
Identifying records by slugs is a two step problem. Given a Router mapping that looks like this:
App.Router.map(function() {
this.route('post', { path: '/post/:post_slug' });
});
First the Router needs to know how to look up the record by the slug
using the :post_slug
param. Then link-to
needs to generate an
anchor tag with the record's slug property in place of the
:post_slug
.
Querying Records by Slug
By default, Ember Data does not provide a way to look up only 1 record
by a property (other then the id property). Luckily, it is easy to
extend Ember Data's store object to provide this functionality. The
code below adds a findOne
method to the store.
App.ApplicationStore = DS.Store.extend({
findOne: function() {
return this.find.apply(this, arguments).then(function(results) {
return results.objectAt(0);
});
}
});
Using findOne
we can easily fetch a record by its slug
property in
the Route's model hook.
App.PostRoute = Ember.Route.extend({
model: function(params) {
return this.store.findOne('post', {slug: params.post_slug});
}
});
Linking To the Slug
The next step is to include a record's slug in the anchor tag generated by ember's link-to helper. The normal way to create a link-to would look something like this:
Unfortunately it will generate an anchor tag that includes the Post
's
id
in the dynamic segment.
<a href="/post/1">hamster</a>
You can work around this behavior by defining a custom serialize
method on the route.
App.PostRoute = Ember.Route.extend({
serialize: function(model) {
return {
post_slug: model.get('slug')
};
}
});
Now using we can use the same link-to
code as above and the
records's slug property will be correctly serialize the :post_slug
param in the anchor tag's href.
<a href="/post/hamster">hamster</a>