Nested Layouts
Nested layouts allow you to DRY up your views by having custom per-controller layouts that ultimately share a main layout. The folks at New Bamboo have a tutorial on this subject. We’re going to modify this approach slightly, but let’s first discuss layouts in Merb.
Merb and Layouts
Merb, like Rails, looks for a layout of the same name as the controller that is currently being used. Suppose we have a People controller. Merb will look in app/views/layout for a file named people.html.erb. If this is found, Merb will render it first. If Merb cannot find this file, it will default to application.html.erb. This can be overridden with the layout method. For example
class People < Application
layout 'my_swanky_layout' # app/views/layouts/my_swanky_layout.html.erb
end
The New Bamboo Solution
The New Bamboo solution recognizes that you get a free, per-controller layout option simply by having a my_controller.html.erb in app/views/layouts, and that app/views/layouts/application.html.erb is a good place for a “super” layout — that is, the layout that we will “inherit” from.
Let’s suppose our application.html.erb looks like this:
<html>
<head><title>Woo!</title></head>
<body>
<h1>Welcome to my page!</h1>
<div id="navigation">
<a href="/pages">pages</a> <a href="/people">people</a>
</div>
<%= catch_content :for_layout %>
</body>
</html>
Clearly we might want to change the navigation depending on the controller (e.g. for an admin controller). The New Bamboo solution recommends leveraging the throw_content method in a layout file of the particular controller and then catching and rendering the content, as such:
<%= throw_content :navigation_links do %>
<div id="navigation">
<a href="/spam">Spam</a>
<a href="/eggs">Eggs</a>
</div>
<%= catch_content :for_layout %>
<% end =%>
<%= render catch_content(:navigation_links), :layout => 'application' %>
This is not a bad solution, but there were some troubles found:
1. It’s repetitive. Why are we catching :for_layout twice?
2. It sometimes rendered the navigation elements twice.
A Better Solution
Sadly, this solution doesn’t work. Any help perfecting this technique is appreciated.
A better solution (though still not perfect) is to change your controller layout file to look something like this:
<%= throw_content :navigation_links do %>
<div id="navigation">
<a href="/spam">Spam</a>
<a href="/eggs">Eggs</a>
</div>
<% end =%>
<%= render '', :layout => 'application' %>
Which basically says “throw this content, and render the application layout”. We just need to change our application.html.erb slightly:
<html>
<head><title>Woo!</title></head>
<body>
<h1>Welcome to my page!</h1>
<div id="navigation">
<%= catch_content :navigation_links %>
</div>
<%= catch_content :for_layout %>
</body>
</html>
And that should do it.