Flexible page layouts using Ruby on Rails
Your webapp would normally have some dynamic pages and a defined layout. A layout defines where to put the banner, the main navigation, dynamic content, footer, and other secondary contents like announcements. As a developer, you could be assigned to work one dynamic page (e.g. 'projects'), your buddy on another page (e.g. 'tasks'), and the designer on the main navigation and banner image.
The most common approach (at least from my experience) is to put the different parts of the layout in separate files and agree how each part will be included in every dynamic page. For example, let's have a simple layout where we have a 'header', 'content', and 'footer'. Let's say you are assigned to work on the 'projects' page. Let us also assume you are using PHP for this application. In you 'projects.php', you will then have the following code:
-
<?php include('header.php') ?>
-
-
This is where you put the list of projects.
-
-
<?php include('footer.php') ?>
Now, your buddy who is working on the 'tasks' page will have a similar code in 'tasks.php'.
-
<?php include('header.php') ?>
-
-
This is where you put the list of tasks.
-
-
<?php include('footer.php') ?>
Sidebar: The popular blog tool Wordpress uses this approach in customizing its templates.
What's wrong with this approach (or why I don't like it)?
If you are working on 100 dynamic pages, each page will have to repeat this pattern over and over again. If you forgot to include the 'header', then your page will not display properly. Of course, that can be easily remedied by copy-and-paste.
If you were assigned to work on 'projects' page, the ideal scenario is you focus on the content and not worry about whether there is a left sidebar (or a right), or the 'header' comes before the 'footer'. As much possible, you must be isolated from the details of the layout.
Let's enhance our original layout and add support for a sidebar. Unlike a 'footer', the content of a 'sidebar' is usually dynamic and is dependent on the main content. For example, if you are working on a 'projects' page, you may want to have a list of 'done projects' on the sidebar. A sensible approach would be to use the '<div>' tag to group the main contents and sidebar contents. In your 'projects.php', your code will look something like this:
Not only you have to repeat this pattern for every dynamic page, every page is now tied to a specific structure. What if you decided to change '<div>' attribute from 'id' to a 'class'. Or what if you just found out about the IE box model bug and now have to use another enclosing '<div>' inside 'main' and 'sidebar'? To work around the box model bug, your code can look like this:
Aside from the repetition and dependency problem, the implementation of the 'header' and 'footer' is ugly. This is how your header.php would look like.
-
<html>
-
<body>
-
<div id="header">
-
Banner and navigation here.
-
</div>
-
-
<div id="content"> <!-- note this tag is not properly closed -->
This is your footer.php
-
</div> <!-- yikes! -->
-
</body> <!-- yikes again! -->
-
</html> <!-- enough! I get the point -->
The Ruby on Rails way
In the approach we have just discussed, in my opinion you are not actually defining a layout. Instead, you are just suggesting conventions that you hope everybody in your team would follow. You can't stop a developer from including the 'footer' before the 'header', right?
Ruby on Rails uses an approach that isolates the contents with the structure of the layout. CakePHP, according to my buddy Evan, also has this approach to layouts. Using RoR, you define a layout that looks like this:
In your 'projects' page (in RoR that would be 'projects.rhtml'), all you need to do is put your content.
-
# inside your projects.rhtml file
-
These are all my projects.
That's it. For every page, you focus on displaying the contents; there is no need to include 'header" or "footer". Another nice thing about RoR layout is when you look at the layout file, you can see the whole thing in a single file. Unlike in the previous approach where the header and footer are in two files. In RoR, it is easier to check whether you have the right HTML tags (notably the start and end tags) in place.
Of course, you can also modify the layout to add more elements like top navigation and banner.
Now, how would we define the placeholder for sidebar contents? We cannot go back to the previous approach because that would make the code dependent on the structure. In RoR, we can make our own 'content_for' markup to separate the different content types. First, we define where the 'contents' would be placed in our layout file.
In your 'projects' page, you would then decide what contents to put in the 'main', and what to put in the 'sidebar'.
-
<% content_for("main") do -%>
-
These are the projects.
-
<% end -%>
-
-
<% content_for("sidebar") do -%>
-
These are other information about the projects
-
<% end -%>
Your existing dynamic pages are not entirely free of changes but with this approach, you are not dependent on the structure of the page. It does not matter if the 'sidebar' is coded before the 'main' because it is up to the layout to decide where to put the contents. If you want to add more tags, you just adjust the layout. You can also experiment on a variety of layouts (e.g. sidebar on the left) without affecting the dynamic content pages.
After I've written this post (using Notepad++ on WinXP), I checked the online Ruby on Rails API documentation and found out that '@content_for' is deprecated. The preferred way to do this now is '<%= yield :main %>' in place of '@content_for_main'.
In my next Ruby on Rails post, I will show how to use Ruby's 'yield' to compose the different parts of your web page in a reusable manner.
Did you enjoy this post? Why not leave a comment below and continue the conversation, or subscribe to my feed and get articles like this delivered automatically each day to your feed reader. If you don't have a feed reader, you can always have these articles delivered to your email inbox every day. Click here to sign up.



Nice post. But you know, you can already implement RoR’s flexible layout in PHP ..that is of course, using Symfony Framework.
I’m looking forward to your other RoR tips and tricks. Learning through somebody else’s experience is good. =)
Cheers!
That’s quite interesting. I usually stick to just the default layouts in my projects but it does tend to lead to a wee bit repetition and copy/pasting which I don’t like =)
By the way Greg, for one of my projects one of my clients is demanding a lot of graphic design work but wants it done cheap. I’ve been busy looking for a graphics designer from the Philippines who I can coordinate with. Do you happen to know anyone who’s available for contract work in this area? Let me know.
take note that, as of the latest rails, the recommended approach is to use “yield” instead.
Hi Cris,
Nice of you to drop by. Don’t worry, more tips to come.
Hi Raymond,
You could actually use a single layout for all your controllers.
Hi Raichu,
You’re correct. I did mention it in the 2nd to the last paragraph.