Understanding Ember routes
Asked Answered
P

2

2

Assume I have routemap like this:

App.Router.map ->
    @resource 'posts', ->
        @resource 'post', {path: '/:post_id'}

So I have list of posts on /posts route and a single post on posts/2. By default posts template will be rendered to {{outlet}} of a parent template (application) which is OK and post template to {{outlet}} of posts template which is not what I want. I want to replace list of posts with a single post.

So I came with that solution to set desired template to render to it's {{outlet}}:

App.PostRoute = Em.Route.extend
    renderTemplate: ->
        this.render(into: 'application')

But as a result I have list of posts and a single post rendered into one {{outlet}}. I can emtpy {{outlet}} each new route but then it breaks back button, cuz Ember wont render previous template again, assuming that is already rendered.

So questions is how to use single {{outlet}} for different routes and don't break ember's logic. Of course I can avoid nested routes but they a really usefull when you need semantic links like this post/2/comment/12/edit.

Piquant answered 12/5, 2013 at 18:23 Comment(0)
B
3

If your templates are not nested, you shouldn't nest your routes, or you'll end up fighting Ember. Acting as Ember's state manager, routes describe your app structure. Nesting should follow your UI, not how you want your URLs to look like.

If your concern is URLs, you can just modify the path to fit your needs:

App.Router.map ->
  @resource 'posts'
  @resource 'post', { path: 'posts/:post_id' }
Borchardt answered 12/5, 2013 at 18:40 Comment(3)
But what if the parent route has parameters? If you have 'posts/:user_id' and then '/posts/:user_id/post/:post_id' I can't get the linkTo helper to generate a proper link like '/posts/1/post/3' instead I get '/posts/undefined/post/undefined'.Hogle
According to the linkTo helper documentation, you can pass multiple model arguments for paths with multiple dynamic segments, but that only seems to work when the resources/routes are nested. When I try it with the above arrangement I get this: More context objects were passed than there are dynamic segments for the route. There has to be an idiomatic Ember.js way to have nested URLs without nested templates (?).Hogle
Furthermore, with this approach, the child resource can no longer access the parent with a simple property like "needs: 'posts'" in the controller. That is a fairly common need, if only to show a breadcrumb to navigate back to the parent. Sorry, didn't mean to hijack the conversation.Hogle
S
0

The approach I have used for handling nested and non nested templates even when my routes are nested is to use named outlets. Ember makes it really easy.

I have a top level menu outlet that always holds a menu relevant to the route being visited.

Example Application Template:

<div id="nav">
  {{partial "nav"}}
</div>
<div id="menu">
  {{outlet "menu"}}
</div>
<div id="main">
  {{outlet}}
</div>
{{outlet "modal"}}

I have two named outlets, "menu" and "modal", which are used to hold content which is not nested but used by nested routes or any route to be precise. Any route can render a modal in response to actions into the global "modal" outlet, and similarly any route can render a menu into the "menu" outlet.

My examples use coffeescript.

Router:

App.Router.map ->
  @resource "posts", ->
    @route "create"
    @resource "post", {path: ':id'}, ->
      @resource "comments", {path: ':id'}, ->
        @route "create"
      @resource "comment", {path: ':id'}, ->

Routes which render a menu into a named outlet that is not nested:

App.PostsIndexRoute = Em.Route.extend
  renderTemplate: (controller, model)->
    # default render
    @_super arguments...
    # render posts menu into the application menu outlet
    @render "posts.menu",
      outlet: "menu"
      into: "application"

App.CommentsIndexRoute = Em.Route.extend
  renderTemplate: (controller, model)->
    # default render
    @_super arguments...
    # render comments menu into the application menu outlet
    @render "comments.menu",
      outlet: "menu"
      into: "application"

You don't have to do the default nested rendering as above, you could just define a route type that always renders into a named outlet such as "content" (just don't call it "main" because Ember uses that).

App.ContentRoute = Em.Route.extend
  renderTemplate: (controller, model)->
    @render outlet: "content", into: "application"

And then subclass from it for any routes that should always render into the application 'content' outlet:

App.PostsIndexRoute = App.ContentRoute.extend()
App.CommentsIndexRoute = App.ContentRoute.extend()

But it would be better to do it with a mixin so you can easily include whatever concerns you wish into any route (eg authenticated route concerns).

App.RenderIntoContent = Em.Mixin.create
  renderTemplate: (controller, model)->
    @render outlet: "content", into: "application"

App.PostsIndexRoute = Em.Route.extend App.RenderIntoContent,
  ...

App.CommentsIndexRoute = Em.Route.extend App.RenderIntoContent,
  ...
Saavedra answered 2/5, 2014 at 2:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.