Wednesday, August 1, 2007

Blockish helpers from HTML hell

There is one little thing about the link_to function and her friends. One that does not strike you particulary often. They all take string as their first parameter. This is not a problem in most usual cases. But in case you have a complicated HTML layout and you want your Rails application to generate something like this:


<a class="points" href="#" onclick="new Ajax.Updater(...); return false;">
<span class="number" id="points"> 1 </span>
<span class="number" style="display: none;" id="indicator">
<img alt="woking..." src="/images/indicator.gif" />
</span>
<span class="desc">
description
</span>
</a>


Well then you will yourself in a tight spot. It is kinda harsh to do link_to_remote q{...all that stuff above...}, right? It needs something different, something like a BLOCK! When I first saw this code that I was supposed to eRubize I thought it wants something like the form_tag :action => "some" do ... end. Well unfortunately Rails have no such thing for links. Pity. But do not despair, its quite simple to create ourselves a simple pretty solution. First lets start with what do we want our code to look like:


<% blockish_link_to_remote :update => "some_tag", :otther_options => values do %>
<span class="number" id="points"> <%= item.points %></span>
<span class="number" style="display: none;" id="indicator">
<%= image_tag "indicator.gif", :alt => "working" %>
</span>
<span class="desc">
<%= item.descripion %>
</span>
<% end %>


That looks actually pretty cool. Now we need to write us a blockish_link_to_remote helper. Not a hard job, really! First we need to take a look at two other methods. First it will be concat (not String#concat actually, see below for details). If you have a template object binding you can use concat to output anything into the templates output. This is pretty usefull because we do not want to write <%= before out blockish_link_to_remote, thats just not pretty, so the helper will have to find another way to output its inner block contents.

The second method is capture. This method lets our helper execute its inner block and literally capture its output into a String. After that we will have everything we need. The longer way how to write our blockish_link_to_remote helper would be:


def blockish_link_to_remote(params, options, &block)
block_out = capture(&block)
link_out = link_to_remote(block_out, params, options)
concat link_out, block.binding
end


And now my way. This nice Rubyfied Railsyfied version:


def blockish_link_to_remote(params, options, &block)
concat link_to_remote(capture(&block), params, options), block.binding
end


This was waaay too easy wasn't it? Was it? That's it! Really. Nothing else to it :) Now you can go ahead and create your blockish_link_to helper and show them to me in the comments. Remember when you want to create a link that has a lot of stuff in it, use blocks!

Links for method reference:

ActionView::Helpers::TextHelper.concat
ActionView::Helpers::CaptureHelper.capture

0 comments: