Friday, June 20, 2008

Nested Layouts in Rails


I was wondering how to keep the layouts in my rails application code DRY - I was close to the point of kicking myself for having to make changes to all my layouts whenever something standard changed - so i researched online and found the above diagram by Matt McCray in his blog.

I used Matt's method for nesting layouts, but instead of defining a method called sub_layout in my controllers, I called a pre-defined rails controller method:

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

and defined a partial layout named after every controller I had, which had different settings for their own views. Rails has a local variable called 'controller' you can use to access its controller's name and even the action used for the current view. So using this, I don't have to define additional methods in my controllers, nor use any plugins.

kudos to Matt for coming up with this in the first place. Thanks!

3 comments:

Weyus said...

You can take this a step further.

If you want to mix and match your "layouts" (which are really just partials, let's face it) across arbitrary controller/action pairs, you can just define a special variable, say @inner_layout.

And then in each action that you want to use the inner layout, you just define it.

So in your application.html.erb:

<%= @inner_layout ? render(:partial => "layouts/#{@inner_layout}") : yield %>

If there's an inner "layout" (partial) then render it (assuming that it will yield), and if there isn't one, just go ahead and yield.

And then in the appropriate action(s):

@inner_layout = 'some_inner_layout_filename'.

Although the variable thing is a bit of a drag, you could take this as far as you want.

Steven Parkes said...

I didn't need a new nested layout for every controller, just a few. Well, one to start. Simply wrapping the render :partial with a block and catching the MissingTemplate, where you drop back to yield, (seems to) work great:

<%= begin; render :partial => "layouts/#{controller.controller_name}"; rescue ActionView::MissingTemplate; yield; end %>

Vered said...

Just wanted to say THANKS for your great solution - exactly what I needed.
Works perfect!