Written by: steve ross on November 12th 2008
Many Rails developers will create a default layout in application.html.erb and use it as a consistent wrapper for all pages rendered on a site. This is one of the cool features of Rails – consistent rendering of the site’s layout. But what if you want to change things for certain controllers or actions.
Layouts By Controller
This is the trivial case because all you need to do is:
class AdminController < Application
layout 'admin'
def some_action
end
end
and every action within AdminController uses admin.html.erb instead of application.html.erb. But how about a controller that handles some public-facing and some administrative aspects?
Layouts With Exceptions
At first blush, this would also appear to be trivial. Just modify the code above as follows:
class AdminController < Application
layout 'admin', :only => [:some_action]
def some_action
end
def some_public_facing_action
end
end
At first glance this is flawless, and of course you’re testing the action you changed, so you got suckered in. One day, you go to /some_public_facing_action and WTF?? There is no layout at all.
It turns out that what the line
layout 'admin', :only => [:some_action]
really means is apply the ‘admin’ layout to some_action and don’t apply any layout to anything else. So you think, well, How’s about:
class AdminController < Application
layout 'admin', :only => [:some_action]
def some_action
end
def some_public_facing_action
render :layout => 'application'
end
end
Bzzzt! Wrong answer. Rails already believes the public facing action is exempt from layout.
Using More Than One Layout in A Controller
The best way I’ve found to do this is to write a helper that determines on an individual basis which methods require which layout. Here’s how:
class AdminController < Application
layout :smart_layout
def some_action
end
def some_public_facing_action
render :layout => 'application'
end
protected
def smart_layout
admin_actions = [:some_action]
admin_actions.include?(action_name) ? 'admin' : 'application'
end
end
You may notice that I have only one action in my array, but more commonly, there will be multiple actions.
Further Reading
See Rails Guides for an in depth discussion of how layouts are inherited and controlled.