3
votes

Using Ember.js v1.2.0, I'm trying to render a sidebar template into a named outlet. The template can possibly be different based on the current route. I'm struggling to come up with the "Ember" way to do this.

Here's the gist of what I have so far:

app/templates/application.hbs

{{outlet sidebar}}

app/routes/application.js

var ApplicationRoute = Ember.Route.extend({
  renderTemplate: function(controller, model) {
    var sidebar = controller.get('sidebar') || 'application';
    this._super();
    this.render('sidebar/' + sidebar, {
      into: 'application',
      outlet: 'sidebar'
    });
  }
});

app/routes/docs.js

var DocsRoute = Ember.Route.extend({
  renderTemplate: function(controller, model) {
    this.controllerFor('application').set('sidebar', 'docs');
    this.render();
  }
});

This doesn't really work, and it doesn't feel very Ember-ish. Help?

2

2 Answers

6
votes

If I undertsand the question correctly then sidebar is always defined by the main route. Then you can set sidebar in each route's renderTemplate:

var ApplicationRoute = Ember.Route.extend({
  renderTemplate: function(controller, model) {
    this.render();
    this.render('sidebar/application', {
      into: 'application',
      outlet: 'sidebar'
    });
 }
});

var DocsRoute = Ember.Route.extend({
  renderTemplate: function(controller, model) {
    this.render();
    this.render('sidebar/docs', {
      into: 'application',
      outlet: 'sidebar'
    });
 }
});

var AnotherRoute = Ember.Route.extend({
  renderTemplate: function(controller, model) {
    this.render();
    this.render('sidebar/another', {
      into: 'application',
      outlet: 'sidebar'
    });
 }
});

If you have very many of these routes and you don't want to write renderTemplate manually each time then you can define abstract route class that does this automatically and extend that class in each route that needs sidebar.

var RouteWithSidebar = Ember.Route.extend({
  routeName : function() {
    var routeName = this.constructor.toString();
    // Remove 'Route'
    routeName = routeName.replace('Route', '');
    // Make first letter lowercase
    routeName = routeName.charAt(0).toLowerCase() + routeName.substr(1);
    return routeName;
  },
  renderTemplate: function(controller, model) {
    this.render();
    this.render('sidebar/' + this.routeName(), {
      into: 'application',
      outlet: 'sidebar'
    });
 }
});
1
votes

I recently wrote a blog post entitled Model-based sidebars in Ember. A much better approach is to use nested routes, where the parent route renders the sidebar:

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

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

Router.map(function() {  
  this.route('authors', {path: '/authors'}, function() {
      this.route('books', {path: '/:account_id/books'});
  });
});

export default Router;

This is much more succinct than using named outlets and doesn't require messing around with the renderTemplate function of your routes. Moreover, the accepted answer becomes quite convoluted when you need to show model data in the sidebar, since each route would then require more than one model.

The blog post has more details and a link to an example on GitHub.