Multiple outlets in Ember.js v2 router-Collection of common programming errors
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 withUncaught TypeError: Object 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 ownsetupController
method) and callcontroller.connectOutlet
here. This fails as described above; the controller object passed into this method does not have aconnectOutlet
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):
Ember.js Router Example
.outlet {
border: 1px solid black;
padding: 5px;
}
Root index template. You should not see this because we redirect App.IndexRoute elsewhere.
About this demo.
Guide to this demo.
Animals. You have selected:
{{ outlet }}
No animal selected.
Cat. I can meow. Like all animals, I
{{ outlet }}
Dog. I can bark. Like all animals, I
{{ outlet }}
am alive.
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}}
{{ outlet }}
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();
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.
-
You can use multiple named outlets. Here’s a jsfiddle example: http://jsfiddle.net/W2dE4/6/.
{{outlet header}} {{outlet body}} {{outlet navBar}}
Also see this answer for some other techniques.
events: { showModal: function(){ this.render('modal', { into: 'index', outlet: 'modalOutlet', controller: this.controllerFor('modal') }); } }
Originally posted 2013-11-23 09:50:46.