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:
{{#link-to 'post' model}}{{model.slug}}{{/link-to}}
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>