Dealing with named outlets


#1

I am trying to build an application where I have multiple named outlets, but I am having trouble in understanding how to instatiate the controller and defining the model in this sceneario. As an example take the template below where I have the single “search” template:

<script type="text/x-handlebars" id="application">
        {{outlet "search"}}
</script>

<script type="text/x-handlebars" id="search">
    THIS IS THE SEARCH
    {{outlet}}
</script>

<script type="text/x-handlebars" id="persons">
    THESE ARE THE RESULTS
</script>

For which I have defined the routes structure:

App.Router.map(function() {
    this.resource("home", { path: "/" });
    this.resource("search", function() {
        this.resource("persons", function() {
           this.resource("person", { path: ":person_id"});
        });
    });
});

App.HomeRoute = Ember.Route.extend({

  renderTemplate: function() {
    this.render();
    this.render('header', { 
        outlet: 'header', 
        into: 'application' 
    });

    this.render('search', {
        outlet: 'search',
        into: 'application'
    });
  }
});

At this point there are three things that go wrong.

  1. The “persons” template is not inserted into the “search” template.
  2. I am not able to render /#/search directly.
  3. The model is not set for search if I introduce a SearchRoute.

I have played around with this.generateController and the setupController hook and what not, but at this point I am quite confused.


#2

Nest the ‘search’ resource in the ‘home’ resource, and that should help. Since they’re not nested right now, the search route doesn’t even know it should render the home route first.

If the persons template still doesn’t show up, you can try to render it manually the same way you’re doing the header and search templates. Once I started using named outlets, a lot of the Ember ‘magic’ stopped happening, so I just called that ‘this.render()’ function everywhere the app. You might not have to, but it’s always there.


#3

Thanks, nesting the “search” resource resolved the issue with rendering #/search, but the person template still doesn’t show up.


#4

You might have to put the persons template in manually:

  App.PersonsRoute = Ember.Route.extend({
      renderTemplate: function() {
        this.render('persons', {  
          into: 'search' 
        });
      }
    });

Once you render multiple templates in a route, the child routes can’t always figure out what {{outlet}} to go in. That’s why I just explicitly use renderTemplate for all the child routes, instead of losing tons of time wondering when it’ll “magically” work. It adds a few lines, but then I know it’s going to work.


#5

The only way I could get it working with all the routes was to basically put everything inside the renderTemplate function:

App.ApplicationAdapter = DS.FixtureAdapter.extend();
App.SearchController = Ember.ObjectController.extend();
App.PersonsController = Ember.ArrayController.extend();

App.HomeRoute = Ember.Route.extend({
  renderTemplate: function() {

    this.render();

    this.render('search', {
        outlet: 'search',
        into: 'application'
    });

    this.controllerFor('persons')
        .set('content', this.store.find("person"));
    
    this.render('persons', {
        into: 'search'
    });

  }
});

Is this really the Ember way of doing things? It def. doesn’t look as appealing as all the TodoMVC application code.


#6

The most ‘Ember way’ would be having a single outlet in every route. You could change the application to be like this:

<script type="text/x-handlebars" id="application">
    {{partial 'header'}}
    {{outlet}}
</script>

Then maybe if all of your templates have only one {{outlet}} without a name, then the nesting will happen more magically.

Then a lot of the stuff you put in the HomeRoute would ideally be in the other routes, like this:

App.HomeRoute = Ember.Route.extend({
  //the application template will automatically go in here
});
  
App.SearchRoute = Ember.Route.extend({
  //the search template will automatically go in the {{outlet}} because there's only one, so it isn't confused which one to go in.
});

App.PersonsRoute = Ember.Route.extend({
  //the persons template will automatically go into the {{outlet}}
    model: function(params, transition) {
        return this.store.find('person');
    }
});

If that doesn’t work right, then you’ll have to blame me and my Friday beers.