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!


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!