Multiple dynamic segments without nesting


#1

The Problem

I want to have a route with two dynamic segments that represent two related models. When I use link-to to generate a link to the route, I get the following error:

Error: More context objects were passed than there are dynamic segments for the route: list

What I’ve tried

nested routes

This works but it’s nested, which means that the list template is rendered in the {{outlet}} of the parent (user) template. I don’t want this.

messing around with model hooks and serialize

I got the list route model() hook to return a hash containing the user and list, and serialize to return an object with the correct values for each dynamic segment, but the same error appeared.

Pass only one object in link-to

Passing only one object (the list) removed the error but resulted in the URL being wrong:

http://localhost:4200/undefined/list-1

Code

The routes are defined as follows:

Router.map(function() {
  this.route('user', { path: ':username' });
  this.route('list', { path: ':username/:slug' });
});

slug is the list slug, a field in the list model.

The user route displays all the lists a user has, where each one links to the list page using the following code in the template:

{{#each model.lists as |list|}}
  {{#link-to "list" model list class='list-group-item'}}
{{/each}}

model is the user returned by the user route’s model() hook:

model(params) {
  return this.store.queryRecord('user', {
      username: params.username
  });
}

The list route model() hook returns the list identified by the slug:

import Ember from 'ember';

export default Ember.Route.extend({
  model(params) {
    return this.store.queryRecord('list', {
      slug: params.slug,
    });
  }
});

The user model is shown below:

import DS from 'ember-data';

export default DS.Model.extend({
  username: DS.attr(),
  lists: DS.hasMany('list', { async: true }),
});

And finally, the list model:

import DS from 'ember-data';

export default DS.Model.extend({
  name: DS.attr(),
  slug: DS.attr(),
  user: DS.belongsTo('user'),
});

#2

This definitely does work. When you link-to, you either have to provide one object, that is then passed to your route’s serialize hook to generate the dynamic segments, or you need to provide two individual params that are primitives. If you get an error saying you passed in too many context objects, it means you are passing in objects not primitives.


#3

Thanks alex. The guide’s explanation of link-to's parameters is a bit confusing:

At most one model for each dynamic segment. By default, Ember.js will replace each segment with the value of the corresponding object’s id property. In the example above, the second argument is each photo object, and the id property is used to fill in the dynamic segment with either 1, 2, or 3. If there is no model to pass to the helper, you can provide an explicit value instead:.

I read At most one model for each dynamic segment to mean each dynamic segment can have at most one model, not that one model is used for all dynamic segments.

I think the documentation could be clearer, and would be really useful to also mention the following:

  • When a model is used, it’s passed to the serialize hook to generate the dynamic segments and the model hook isn’t called
  • When primitives are used, serialize isn’t called and the model hook is called

Here’s a suggestion:

Either a single model, or one primitive per dynamic segment. If a model is used Ember.js will call the route’s serialize hook to generate the dynamic segments; the model hook isn’t called. If primitives are used Ember.js will use them to fill in the dynamic segments; the model hook will be called.

What do you think?


#4

That seems like a better description to me