Monthly Archives: August 2014

Ember basic navigation with nested templates

Everybody has heard of the steep learning curve of the frontend framework Ember. While starting to learn Ember I found that proven true. Often I thought I had understood a particular concept but when I tried to implement it, it just didn’t work.

One thing I was working on was just a basic navigation for a website with nested templates, a main- and a submenu. I couldn’t really find an example with an easy way of implementing that, although it’s something that probably most beginners will try to do. Most examples I found were dealing with datasets and how to bind views to them, for example to build a blog. But I really just wanted a simple navigation and not care about any data yet. So I’m going to try and give an example of how to do this.

A working demo of this example is hosted here.

Top-level navigation and sub-navigation

We are going to build an Ember App with a consistent navigation in every page. Our app will have a main navigation with access to first-section, another-section and about pages. first-section and another-section will also have submenus and more pages nested inside, while about will just be a simple page without further navigation.

Let’s start with the router.js:

App.Router.map(function() {

this.resource('first-section', function () {
  this.route('part-1');
  this.route('part-2');
  this.route('part-3');
});

this.resource('another-section', function () {
  this.route('part-1');
  this.route('part-2');
  this.route('part-3');
});

this.resource('about');
});

Here we define the routes, which are going to be linked in the top level menu, as resources. The resources that will have a submenu and nested templates, have additional routes associated.

Nested templates

The top-level (application) template, which contains the main navigation, looks like this:

<script type="text/x-handlebars">
  <header class="main-app-header">
    <h1>Ember navigation, subnavigation and nested templates example</h1>
    <ul class"navigation main-navigation">
      <li class="nav-item">{{#link-to "first-section"}}First Section{{/link-to}}</li>
      <li class="nav-item">{{#link-to "another-section"}}Another Section{{/link-to}}</li>
      <li class="nav-item">{{#link-to "about"}}About{{/link-to}}</li>
    </ul>
  </header>

  <div class="main-content">
    {{outlet}}
  </div>
</script>

It uses the link-to helper to redirect to the section-resources we defined in router.js. On the bottom we indicate with {{outlet}} where the  templates that belong to these resources are going to be rendered.

These will contain the sub-navigation and look like this:

<script type="text/x-handlebars" id="first-section">
  <header class="sub-header">
    <h2>First Section</h2>
    <ul class"navigation sub-navigation">
      <li class="nav-item">{{#link-to "first-section.part-1"}}Part 1{{/link-to}}</li>
      <li class="nav-item">{{#link-to "first-section.part-2"}}Part 2{{/link-to}}</li>
      <li class="nav-item">{{#link-to "first-section.part-3"}}Part 3{{/link-to}}</li>
    </ul>
  </header>

  <div class="main-content">
    {{outlet}}
  </div>
</script>

This looks similar to the top level template, with the difference that the routes which are linked here, contain of two parts separated by a dot: first-section.part-1.

The lowest level templates are most easy and just contain some text:

<script type="text/x-handlebars" id="first-section/part-1">
  First section part 1.
</script>

Pay attention to the id: "first-section/part-1". Here you have to indicate by the name that the template will be nested in the first-section resource. Also pay attention to the fact that the ids of the nested templates are separated by a / when you name the templates, but when you call the routes with the link-to helper, they are separated by a dot:  "first-section.part-1".

It’s all about naming

When solving this task, I was mostly struggling with the right naming of things. I often got this error: TypeError: Cannot read property 'connectOutlet' of undefined. I didn’t understand what was happening and thought the solution to this was to specify in the route files of every route/template into which parent template the route’s template should be rendered. But it got even more messy. In the end I discovered that the proper naming of all templates and routes would resolve all my problems.

You can review the whole code on github. While solving this problem, I also found this discussion helpful.

Originally I did this example to transfer it later to a version in Ember App Kit. But since Ember App Kit is now outdated and replaced by Ember CLI, I didn’t write about that. You can find the Ember App Kit version here though.