home
  • Blog
2.10
  • Getting Started
  • Tutorial
  • The Object Model
  • Routing
    • Introduction
    • Defining Your Routes
    • Specifying a Route's Model
    • Rendering a Template
    • Redirecting
    • Preventing and Retrying Transitions
    • Loading / Error Substates
    • Query Parameters
    • Asynchronous Routing
  • Templates
  • Components
  • Controllers
  • Models
  • Application Concerns
  • Testing
  • Ember Inspector
  • Addons and Dependencies
  • Configuring Ember.js
  • Contributing to Ember.js
  • Glossary
Old Guides - You are viewing the guides for Ember v2.10.0.
Go to v5.0.0

Specifying a Route's Model

Edit pencil

Often, you'll want a template to display data from a model. Loading the appropriate model is one job of a route.

For example, take this router:

app/router.js
Router.map(function() {
  this.route('favorite-posts');
});

To load a model for the favorite-posts route, you would use the model() hook in the favorite-posts route handler:

app/routes/favorite-posts.js
import Ember from 'ember';

export default Ember.Route.extend({
  model() {
    return this.get('store').query('post', { favorite: true });
  }
});

Typically, the model hook should return an Ember Data record, but it can also return any promise object (Ember Data records are promises/), or a plain JavaScript object or array. Ember will wait until the data finishes loading (until the promise is resolved/) before rendering the template.

The route will then set the return value from the model hook as the model property of the controller. You will then be able to access the controller's model property in your template:

app/templates/favorite-posts.hbs
<h1>Favorite Posts</h1>
{{#each model as |post|}}
  <p>{{post.body}}</p>
{{/each}}

Dynamic Models

Some routes always display the same model. For example, the /photos route will always display the same list of photos available in the application. If your user leaves this route and comes back later, the model does not change.

However, you will often have a route whose model will change depending on user interaction. For example, imagine a photo viewer app. The /photos route will render the photos template with the list of photos as the model, which never changes. But when the user clicks on a particular photo, we want to display that model with the photo template. If the user goes back and clicks on a different photo, we want to display the photo template again, this time with a different model.

In cases like this, it's important that we include some information in the URL about not only which template to display, but also which model.

In Ember, this is accomplished by defining routes with dynamic segments.

Once you have defined a route with a dynamic segment, Ember will extract the value of the dynamic segment from the URL for you and pass them as a hash to the model hook as the first argument:

app/router.js
Router.map(function() {
  this.route('photo', { path: '/photos/:photo_id' });
});
app/routes/photo.js
import Ember from 'ember';

export default Ember.Route.extend({
  model(params) {
    return this.get('store').findRecord('photo', params.photo_id);
  }
});

In the model hook for routes with dynamic segments, it's your job to turn the ID (something like 47 or post-slug) into a model that can be rendered by the route's template. In the above example, we use the photo's ID (params.photo_id) as an argument to Ember Data's findRecord method.

Note: A route with a dynamic segment will always have its model hook called when it is entered via the URL. If the route is entered through a transition (e.g. when using the link-to Handlebars helper/), and a model context is provided (second argument to link-to), then the hook is not executed. If an identifier (such as an id or slug/) is provided instead then the model hook will be executed.

For example, transitioning to the photo route this way won't cause the model hook to be executed (because link-to was passed a model/):

app/templates/photos.hbs
<h1>Photos</h1>
{{#each model as |photo|}}
  <p>
    {{#link-to 'photo' photo}}
      <img src="{{photo.thumbnailUrl}}" alt="{{photo.title}}" />
    {{/link-to}}
  </p>
{{/each}}

while transitioning this way will cause the model hook to be executed (because link-to was passed photo.id, an identifier, instead):

app/templates/photos.hbs
<h1>Photos</h1>
{{#each model as |photo|}}
  <p>
    {{#link-to 'photo' photo.id}}
      <img src="{{photo.thumbnailUrl}}" alt="{{photo.title}}" />
    {{/link-to}}
  </p>
{{/each}}

Routes without dynamic segments will always execute the model hook.

Multiple Models

Multiple models can be returned through an RSVP.hash. The RSVP.hash takes parameters that return promises, and when all parameter promises resolve, then the RSVP.hash promise resolves. For example:

app/routes/songs.js
import Ember from 'ember';
import RSVP from 'rsvp';

export default Ember.Route.extend({
  model() {
    return RSVP.hash({
      songs: this.get('store').findAll('song'),
      albums: this.get('store').findAll('album')
    });
  }
});

In the songs template, we can specify both models and use the {{#each}} helper to display each record in the song model and album model:

app/templates/songs.hbs
<h1>Playlist</h1>

<ul>
  {{#each model.songs as |song|}}
    <li>{{song.name}} by {{song.artist}}</li>
  {{/each}}
</ul>

<h1>Albums</h1>

<ul>
  {{#each model.albums as |album|}}
    <li>{{album.title}} by {{album.artist}}</li>
  {{/each}}
</ul>
left arrow
Defining Your Routes
Rendering a Template
right arrow
On this page

  • Dynamic Models
  • Multiple Models
Team Sponsors Security Legal Branding Community Guidelines
Twitter GitHub Discord Mastodon

If you want help you can contact us by email, open an issue, or get realtime help by joining the Ember Discord.

© Copyright 2023 - Tilde Inc.
Ember.js is free, open source and always will be.


Ember is generously supported by
blue