cult3

Transitioning to new pages in Ember

Jul 20, 2015

Table of contents:

  1. Writing the test
  2. Add the route to the router
  3. link-to in template
  4. Add the Route
  5. Add The Mirage route
  6. Conclusion

Over the last couple of weeks we’ve built out the main task list using TDD.

First we built out the task list using static HTML (Writing your first Ember.js Acceptance Test).

Next, we made it dynamic by returning data from the Controller (Introducing Controllers and Models to an Ember.js Application).

Next, we mocked an API Server by hijacking the Ajax requests and returning responses Mocking your JSON API with Ember CLI Mirage).

Last week we looked at how we can modify the mock server to include the related replies for each task (Adding related models to an Ember Application).

Interactivity is a big part of modern web applications. The big benefit of using a client side framework like Ember is how easy it is to add interactivity.

This includes transitioning to other pages in a seamless way.

In today’s tutorial we’re going to look at implementing the functionality for transitioning to a single task using TDD.

Writing the test

As we’re using TDD the first thing we need to do is to write the test:

test("can open task", function (assert) {
  server.createList("task", 3);

  visit("/");
  andThen(function () {
    click(".task-list .task:eq(0) .task-title a");
  });

  andThen(function () {
    assert.equal(currentURL(), "/tasks/1");
  });
});

First we set up the Mirage database with tasks. If you’re unfamiliar with using Ember CLI Mirage, take a look at this tutorial.

Next we can use the Ember helper function to click on the task title (which should be a link).

We could probably make this selector easier, but this will do for now.

Next we assert that the current url is correct. This means we successfully transitioned to the correct page.

If you run this test you will see it fail. With a failing test in place we can start to implement the functionality!

Add the route to the router

First thing I will do will be to add a route to the router.

import Ember from "ember";
import config from "./config/environment";

var Router = Ember.Router.extend({
  location: config.locationType,
});

export default Router.map(function () {
  this.route("tasks", { path: "/" });
  this.route("task", { path: "/tasks/:task_id" });
});

This won’t be a nested route because I want it to sit under /tasks/id, whereas I want the top level to sit under the index. This isn’t a big issue, but it does make the router look a bit messy.

To display a single task we will need to grab the id from the URL. To include a dynamic segment, you prepend the value with a colon.

Next we can add the link to the template. To add a link to a template we can use Ember’s link-to helper method.

This will automatically generate the link to the correct page based upon the route name.

<h2>Task List</h2>
{{#link-to 'new'}}Create a new task{{/link-to}}
<section>
  <ul class="task-list">
    {{#each task in open}}
    <li class="task">
      <span class="task-title"
        >{{#link-to 'task' task}}{{task.title}}{{/link-to}}</span
      >
      <span class="task-replies">{{task.replies.length}}</span>
    </li>
    {{/each}}
  </ul>
</section>

First we pass the name of the link, and then because we want to link to a particular task, we need to pass the Task object. The helper method will automatically use the Task id.

If you are unfamiliar with Ember’s link-to helper, take a look at my tutorial on Ember Templates (Using Templates in Ember).

Add the Route

Next we need to add the Route. The easiest way to do this is to use the Ember Generator:

ember g route task

In Ember, it is the Route that is responsible for getting the data. We covered the responsibilities of Routes in Routes and Resources in Ember.

Within each Route class we have access to this.store, which is the main interface into Ember Data.

To find a particular Task, we can pass the task_id from the Route params. This will automatically make a GET request to api/tasks/id:

import Ember from "ember";

export default Ember.Route.extend({
  model: function (params) {
    return this.store.find("task", params.task_id);
  },
});

Add The Mirage route

Finally we need to tell the Mirage mock server about the new route.

To do this we need to define a new route in the config.js file under the Mirage directory.

Mirage is very clever and so for standard routes like this we don’t need to do anything fancy:

this.get("/api/tasks/:id");

Now if you run the tests again, or fire up the application in the browser you should now be able to click on any task to display that individual task on it’s own page!

Conclusion

As you can see, Ember is making our lives a lot easier. Ember provides a number of helpers to make testing very simple. Methods like find, click, currentURL are really useful.

Everything we looked at today was really just putting the Ember building blocks in place, we haven’t really done a great deal to make this work.

Using a framework like Ember provides you with these building blocks so you can very quickly build your application without having to worry about the underlying foundation.

Philip Brown

@philipbrown

© Yellow Flag Ltd 2024.