Multiple outlets in Ember.js v2 router

ember.js, javascript

Solution

You can use multiple named outlets. Here's a jsfiddle example: http://jsfiddle.net/W2dE4/6/.

<script type="text/x-handlebars" data-template-name="application">
    {{outlet header}}
    {{outlet body}}
    {{outlet navBar}}
</script>

Also see this answer for some other techniques.

events: {
    showModal: function(){
        this.render('modal', {
            into: 'index',
            outlet: 'modalOutlet',
            controller: this.controllerFor('modal')
        }); 
    }
}

Problem

I'm just getting started with Ember.js and one thing that seems promising is the idea of multiple outlets combining multiple templates mixed together to produce a complex but modular layout. I can't get it to work, though. It seems like there were many questions, answers, and examples on this a few months ago (mid 2012) but in the march to 1.0 they very recently (December 2012/January 2013) rewrote the router to a "v2" API. The docs are good at what they do describe but omit a lot of big picture context, and I have yet to find a single end-to-end example. Here's what I've read: - everything under the Routing guide (up to date, but not exhaustive) - "outlet" template helper api reference (this may be out of date? Every attempt I've made to call `controller.connectOutlet()` fails with `Uncaught TypeError: Object <(generated animals.cats controller):ember170> has no method 'connectOutlet'`. - announcement of Ember.js Router API v2. Specifically the bottom couple comments (question and answer on multiple outlets). Yes, this gist is marked "Warning; outdated; for up-to-date information see the routing guide". But the current routing guide doesn't seem to completely describe the behavior. The Rendering a template section of the guide shows how to render to different outlets that already exist (and I can get this to work), but I can't figure out how to connect additional outlets or instantiate additional templates. What does work for me: - Setting up nested routes (well, nested resources; you can't nest routes; but you can customize routes for the nested resources), and nesting templates and outlets that are automatically instantiated according to the routes. What I have not been able to figure out how to accomplish: - Manually instantiate templates and connect them to outlets. This seems necessary if you want to use multiple outlets, or if you want to have a structure your outlet/template relationships differently than your routes. (There will be an example of this below. Essentially what I'm trying to do is use a template as a mixin that I can embed wherever else I want.) The thing that seems promising but fails for me is - Override a route's controller (extend the route using `App.WhateverRoute = Ember.Route.extend()`, supply my own `setupController` method) and call `controller.connectOutlet` here. This fails as described above; the controller object passed into this method does not have a `connectOutlet` method. Example (here as a jsFiddle, or below as a self-contained html document which embeds the CSS and scripts and loads Ember and dependencies from https links, so you should be able to just save to a local file and open in a browser if you want to try it): ``` <!DOCTYPE html> <html lang="en"> <head> <title>Ember.js Router Example</title> <style> .outlet { border: 1px solid black; padding: 5px; } </style> </head> <body> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script> <script src="https://raw.github.com/wycats/handlebars.js/1.0.rc.2/dist/handlebars.js"></script> <script src="https://raw.github.com/emberjs/ember.js/release-builds/ember-1.0.0-pre.4.js"></script> <script type="text/x-handlebars" data-template-name="index"> <p>Root index template. You should not see this because we redirect App.IndexRoute elsewhere.</p> </script> <script type="text/x-handlebars" data-template-name="about"> <p>About this demo.</p> </script> <script type="text/x-handlebars" data-template-name="guide"> <p>Guide to this demo.</p> </script> <script type="text/x-handlebars" data-template-name="animals"> <p>Animals. You have selected:</p> <div class='outlet'>{{ outlet }}</div> </script> <script type="text/x-handlebars" data-template-name="animals/index"> <!-- you will not see this unless you disable App.AnimalsIndexRoute redirect. --> <p>No animal selected.</p> </script> <script type="text/x-handlebars" data-template-name="animals/cats"> <p>Cat. I can meow. Like all animals, I <span class='outlet'>{{ outlet }}</span> </p> </script> <script type="text/x-handlebars" data-template-name="animals/dogs"> <p>Dog. I can bark. Like all animals, I <span class='outlet'>{{ outlet }}</span> </p> </script> <script type="text/x-handlebars" data-template-name="animal_mixin"> <p>am alive.</p> </script> <script type="text/x-handlebars" data-template-name="application"> <div class="container"> <p> Select contents for my outlet: {{#linkTo "index"}}/ (root){{/linkTo}} {{#linkTo "about"}}/about{{/linkTo}} {{#linkTo "guide"}}/guide{{/linkTo}} {{#linkTo "animals"}}/animals{{/linkTo}} {{#linkTo "animals.cats"}}/animals/cats{{/linkTo}} {{#linkTo "animals.dogs"}}/animals/dogs{{/linkTo}} </p> <div class='outlet'> {{ outlet }} </div> </div> </script> <script> App = Ember.Application.create(); App.Router.map(function() { this.resource("about"); this.resource("guide"); this.resource("animals", function() { this.route("cats"); this.route("dogs"); }) }); App.IndexRoute = Ember.Route.extend({ redirect: function() { this.transitionTo('about'); } }); App.AnimalsIndexRoute = Ember.Route.extend({ redirect: function() { this.transitionTo('animals.cats'); } }); App.AnimalsCatsRoute = Ember.Route.extend({ setupController: function(controller, model) { // BUG: this controller object has no connectOutlet method // (uncomment to see this yourself) // controller.connectOutlet('animal_mixin'); } }); App.initialize(); </script> </html> ``` Essentially animal_mixin is a chunk of boilerplate that I want to use repeatedly as a mixin, dropping it wherever I want by putting an outlet there and connecting it to this template. I realize this example is contrived, because I could do it with "inheritance" provided by the nesting structure: the contents of animal_mixin could go directly in the "animals" template, and I wouldn't need to mention it in animals/cats and animals/dogs. That would be fine if I wanted it in all animals, but let's say I had another subroute of /animals that I don't want to include this snippet. Again, the example is contrived but I hope the question and the intent are clear.

Original source

Related problems