M@'s Musings

« Back to blog
 

Sorta Nested Layouts

I was playing around with layouts in Rails the other day… I know, clearly I have too much time on my hands. But anyway, I found that you can fake a nested layout scheme in Rails by delegating view rendering to partials. As an added bonus, you don’t need to hack around with any of Rails' internals to make it work.

For example, let’s say I’m working on an application that has both a public layout and an admin layout. The public layout consists of tabs and a sidebar. The admin layout has different tabs and no sidebar at all. Both layouts share a site header graphic.

With that in mind, here’s how we could structure our layouts and partials:

app/views/layouts/application.rhtml

<html>
  <head>
    <title>Layout Example</title>
  </head>
  <body>
    <div id="header"><!-- shared header code --></div>

    <%= render :partial=>"layouts/#{controller.sub_layout}" %>

    <div id="footer><!-- shared footer code --></div>
  </body>
</html>

Note: I’m getting the partial name from a controller method named sub_layout. I’ll explain that bit in just minute—In the meantime, knowing that it’ll return either ‘public’ or ‘admin’ is enough.

You’ll notice I don’t have a call to <%= yield %> in the layout itself…

So then, your partials (or sub layouts) will look something like this:

app/views/layouts/_public.rhtml

<div id="tabs"><!-- public tabset --></div>
<div id="public-content">
  <div id="sidebar">
    <!-- sidebar content here -->
  </div>
  <%= yield %>
</div>

app/views/layouts/_admin.rhtml

<div id="tabs"><!-- admin tabset --></div>
<div id="admin-content">
  <%= yield %>
</div>

Ah, there’s the <%= yield %>! That’s how you can delegate the view rendering. Basically, you’re using a partial to wrap HTML around the call to <%= yield %>.

OK, in the application.rhtml listing above, I get the partial that works as a sub layout from the controller. To hook that up, in your ApplicationController, you can specify a default sub layout like this:

class ApplicationController < BaseController

  # .. your actions

  def sub_layout
  "public"
  end

end

Then in any administrative controllers, you can override it:

class UsersController < ApplicationController

  # .. your admin-like actions

  def sub_layout
    "admin" 
  end

end

I went ahead a threw together a little example application to better illustrate:

sublayout_example.zip (68 Kb)

Perhaps the example of different tabs and sidebars isn’t most compelling reason to use sub layouts — Which is fine. The key point to all of this is that you can delegate the rendering of your view from a layout to a partial. Which I’m sure you can leverage in all kinds of cool ways…

Posted
Filed under:
6 Comments
Feb 19, 2007
Aaron Pfeifer said...
There's a plugin I often used to achieve nested layouts: http://nested-layouts.rubyforge.org/
Aug 02, 2007
Chirag Patel said...
Thanks Matt for writing this up in such a clear and detailed way! I will be using your method rather than using the nested-layouts plugin. Your method is pretty simple to understand. Second, I prefer using proprietary plugins only when they save significant amounts of time. I may be wrong in this case, but I believe having full control using what's out of the Rails box, will allow easier maintainability if the app grows and more Rails programmers are needed.
Jun 20, 2008
Asyraf said...
Hey matt,
Thanks for the tip - using partials instead of a plugin is way simpler and 'update-proof'.
I used "layouts/#{controller.controller_name}" in my render method instead of defining a new method in my controllers.
wrote about it in my blog, "rubynerds.blogspot.com":url under "nested layouts for Rails". check it out!
Dec 31, 2008
Jacques said...
Very cool. Exactly what I was looking for. Thanks bro!
Mar 22, 2009
prash said...
thanks! this worked very well for me.
several (but not all) of my views required a standard header and footer (separate from the overall site's header and footer) - a prime candidate for nested layouts.
the sub_layout technique worked out very well ...
Jul 21, 2009
Brian Armstrong said...
Nice solution! Cleanest one I've seen, thanks for posting it.

Leave a comment...

Theme by Cory Watilo.
More great Posterous themes at themes.posterous.com.