Using dynamic segments and dynamic models is an important aspect of routes. The following recipe will go over how this can be accomplished.
Before we begin our recipes, we'll need to set up a plugin called Ember CLI Mirage. Ember plugins, also known as add-ons, make it easy to share common code between applications. The Ember CLI Mirage plugin makes it easy to create a mock server so that we can develop, test, and prototype our data. We won't cover too much of this add-on in this chapter. If you'd like more information, you can download it at https://github.com/samselikoff/ember-cli-mirage.
In this example, we'll use the Ember Data's RESTAdapter, not the new JSON API adapter.
application
folder:$ ember install [email protected]
This will install version 0.1.11
of Ember CLI Mirage, Bower, and npm
packages in the application. We'll be using this version for all the examples in the book.
config.js
file in the app/mirage
folder. Add a couple of new routes:// app/mirage/config.js export default function() { this.get('/students'); this.get('/students/:id'); }
The first fake route, /students
, will return all the student data from our Mirage in the memory database. The second fake route, /students/:id
, will return only the data that matches the ID in the URL. This will be used later when we try out dynamic segments with our models.
students
. Create a new file called students.js
in the app/mirage/fixtures
directory:// app/mirage/fixtures/students.js export default [ {id: 1, name: 'Jane Smith', age: 15}, {id: 2, name: 'Erik Hanchett', age: 14}, {id: 3, name: 'Suzy Q', age: 17} ];
This file name, students.js
, matches the route and will be used to load the data in Mirages in the memory database. Be aware that Mirage also supports factories. Factories is an immensely powerful feature that makes it extremely easy to load lots of fake data. The factories can be used in test cases as well.
For simplicity's sake, we'll just use fixture data.
app/mirage/scenarios/default.js
file:// app/mirage/scenarios/default.js export default function( server ) { server.loadFixtures(); }
The default.js
file in the scenarios
folder is used to seed the database in development. The server.loadFixtures()
method loads all the fixture data so that it can be accessible to the /students
route.
Data in our model may never change. On the other hand, that data might change many times depending on interactions with the user. This recipe will cover how to use dynamic segments with your routes and return data from the model.
students
. Then generate an adapter named application
and finally, a route:$ ember g resource students $ ember g adapter application $ ember g route application
This will generate the routes, adapter, and template files needed for this recipe. Keep in mind that by generating the application
route, you will be prompted to overwrite the application.hbs
file. You can select n, for no, when this occurs.
router.js
file with the new dynamic segment route:// app/router.js import Ember from 'ember'; import config from './config/environment'; var Router = Ember.Router.extend({ location: config.locationType }); Router.map(function() { this.route('students', {path: '/students/:student_id'}); }); export default Router;
This new route has a path of /students/:student_id
. This route will extract :student_id
from the URL and pass it to the model hook as the first argument.
For example, let's say that we have a list of students
and we wanted to be able to access each student's data by visiting /students/1
and /students/2
. Each URL would then return the data for that student.
application.js
file in the app/adapters
folder:import DS from 'ember-data'; export default DS.RESTAdapter.extend({ });
This will create a new RESTAdapter
that Ember will use for this example. This will be covered more in the models chapter.
students.js
file in the app/models
folder. This file is our model and will be used to retrieve data from the Mirage mock server that we created earlier:// app/models/student.js import DS from 'ember-data'; export default DS.Model.extend({ name: DS.attr('string'), age: DS.attr('number') });
This creates a new model with two attributes, name
and age
. The model file defines what the data will look like. We'll be covering this more in the Chapter 7, Ember Models and Ember Data. For now, we will be retrieving this data from the mock server.
students.js
file in the app/routes
folder. Add the Ember Data findRecord
method:// app/routes/students.js import Ember from 'ember'; export default Ember.Route.extend({ model(param) { return this.store.findRecord('student',param.student_id); } });
The model
hook here has one argument, param
. The param
argument is student_id
that is passed from the route's URL. Ember Data has a findRecord
method that takes two parameters. The first parameter is the name of the model and the second is the ID.
This model will return the student record with the ID passed to it. We can now use this in our template.
students.hbs
file in the app/templates
folder. Add the model information:// app/templates/students.hbs {{model.name}} {{model.age}}
The {{model.name}}
and {{model.age}}
properties will retrieve the model information passed to the template from the route.
At this point, we should be able to run ember server
and see data when we access http://localhost:4200/students/1
. To make things a little easier, we'll go ahead and create a new route handler for the main application route.
application.js
file in the app/routes
folder:// app/routes/application.js import Ember from 'ember'; export default Ember.Route.extend({ model() { return this.store.findAll('student'); } });
Multiple models
At times, you may want to use multiple models in one route. This can be accomplished using Ember.RSVP.hash
. The hash takes parameters that return promises. When all the parameters are resolved, then Ember.RSVP.hash
is resolved. In the model, it may look like this: return Ember.RSVP.hash({ students: this.store.findAll('student')
, books: this.store.findAll('book')})
. Each model is separated by a comma.
This will allow our application to retrieve all the records from our student model.
application.hbs
file in the app/templates
folder. We'll add an each
iterator that will link to each student's information:// app/templates/application.hbs <h2 id="title">Welcome to Chapter 4</h2> {{#each model as |student|}} {{#link-to 'students' student.id}}{{student.name}}{{/link-to}}<br> {{/each}} {{outlet}}
In this template, we are iterating through all the records in the student
model. We are using each individual student's name as a link to our dynamic segment. The student.id
argument is passed to the link-to
helper.
student.hbs
template will load with the student's information. It will look like the following image:Dynamic models allow data to change depending on the user action. This data will depend on the dynamic segment setup in the router.js
file. The segment is defined in the router
file and passed from the URL to the model hook as its first argument. Ember Data uses the findRecord
method to find the correct record and return it to the template so that it's available for use.