I feel the area where I need to improve most is in cleaning up my views. I feel like I’ve done a good job cleaning up my controllers and models, thanks to posts like Skinny Controller, Fat Model.
I have known about the content_for in rails views. Which basically allows you to have a layout like so:
<html>
<head>
</head>
<body>
<div id="wrapper">
<div id="header">
<% yield :header %>
</div>
<div id="content">
<%= yield %>
</div>
<div id="sidebar">
<% yield :sidebar %>
</div>
<div id="footer">
<% yield :footer %>
</div>
</div>
</body>
</html>
And in your view the following:
<% content_for :header do %>
This would appear for the yield :header call
<% end %>
<% content_for :sidebar do %>
This is the sidebar content
<% end %>
<% content_for :footer do %>
This is the footer content
<% end %>
<%# the rest would just be the content for the plain yield call %>
This is would be my main content
This is nice, but I wanted to find a way to supply default content and this is what I came up with.
def yield_with_default(yield_on, content=nil, &block)
yield_content = eval "@content_for_#{yield_on} || ''"
if block_given?(&block)
concat(yield_content.empty? ? capture(&block) : yield_content, block.binding)
else
Binding.of_caller do |binding|
concat(yield_content.empty? ? content : yield_content, binding)
end
end
end
I placed this in my application_helper.rb file. And now I can add something like this to my view.
# with a block
<% yield_with_default :header do %>
This is my DEFAULT header content
<% end %>
# without a block
<% yield_with_default :header, "This would be my default content" %>
This feels pretty clean to me. But if anybody has any other suggestions I would love to hear them.
I am also looking for more information on best practices with views in Rails. There doesn’t seem to be much information on the subject.
UPDATED: 04/01/2007 – forgot the do in the content_for statements
Another good post, I like the yield_with_default. As far as more best practice posts about views the only other one I know of off hand is from Err – you’ve probably seen it already, but just in case: http://errtheblog.com/post/28
I think I may be having a brain cloud right now, but I don’t see how this would benefit you if you just add yield_with_default to a specific view. Wouldn’t you need to add it to your application.rhtml in order for the header to be used in any view that calls the layout with the yield :header call? And in that case, why not make it a separate partial called by the layout? I’m probably missing the point altogether :-)
Doug – thanks for the link. I have looked at that post before but forgot about it.
Zerohalo – The yield_with_default calls exist in the the main layout file, in this case the application.rhtml file. Then in my view, like /app/views/people/new I would call the following if I want to overwrite the default header behaviour in the application.rhtml file:
Check out haml. http://haml.hamptoncatlin.com/
Looks really sweet.
Joe – I have looked at haml and it looks promising. The issue with moving to haml is that our developers and designers would need to learn a new markup. I wanted to find a best practice with erb/html. I do plan on trying out haml on a project at home.
One thing I do that cleans up my views is I create 2 helper methods for each attribute I display on a page, one for showing it and one for editing it. I use a naming convention where I prefix either edit or show onto the name.
So if I have a form that asks for someone’s name, email and country I’d have three helper methods edit_name, edit_email and edit_country.
The edit_name and edit_email methods would return text boxes with the maxlength set to the same value as validates_length_of would check; or the database column length, whichever is less. I’d also make sure other attributes like title are set too.
The edit_country method would return a drop-down box of all the countries along with the priority countries set as well.
Sometimes I even take it a step further and create a simple custom form builder so I can use form_for with the helpers.
Form views can tend to get messy, but I’ve found this cleans things up and results in more consistent forms across an app.
http://markaby.rubyforge.org/
I prefer to put the default in a partial and render that if yield returned nil:
yield(:side) || render(:partial => ‘default_side’)
Dan – I’ll have to admit that I haven’t used much helpers in the past. They were out sight out of mind for me, until recently. I am excited to use them more and come up with some good practices. I will take your suggestions to heart. I also use the Custom Form Builder, it really helps to clean up the views.
Mark – thanks for the link. I will check it out.
Ryan – The funny thing is that I tried that and it didn’t seem to work for me. I am sure it was a typo on my part. Or, maybe I just wanted a reason write my own Block Helper. I haven’t decided what version I like best, currently I like my version ~ maybe a little biased ~
Great article! This is definitely the nicest way I have seen to deal with layouts and templates while still keeping things as DRY as possible.
I did have one question though. Is there any way to get this to work when calling a parent view using render :template and then using the :action view to populate yields within the template? I have not been able to get this to work in my app. Any help would be much appreciated.
@supaspoida – what are you trying to accomplish? do you have an example? btw, thanks for the comment.