about me

I'm a 34'ish year old web application developer from South Portland, Maine. I love meeting fellow techies, drop me a line if you want to talk shop.

Categories

Ruby on Rails, JQuery UI … and TabsRenderer!

Posted on 05/19 at 05:46 AM

I’m a big fan of JQuery UI and have been using the Tab plugin a lot recently.

However, I found the code to create each Tab a bit repetitious. It became especially ugly in situations where you might want to display a tab conditionally ... first displaying the tab header, then the tab body, and having to re-use the same condition in both places. yuck!

Sooo, since I had recently given a presentation at our local Ruby Users Group on Design Patterns in Ruby, I thought maybe a little template pattern would be a nice fit here. The result: TabsRenderer!!

TabsRenderer is a view helper that lets me build my tabs in a logical order, and then call a special method to return the final result. As well, creating a tab conditionally is a breeze!

I should mention, since I created TabsRenderer as a helper Class and not just a helper method, I had to address the issue of not being able to use Rail’s other helpers within it, as they would be outside my class’s scope. My solution was to borrow a technique I learned recently over at railscasts called ”Refactoring Out Helper Object”.

On to the code ...

5/20/08 - updated with Dietrich’s 3rd param options hash suggestion
6/03/08 - updated with param for options hash on main div as well
6/22/08 - an official repository for this code has been set up at github

Plop this into your helpers directory, and name it tabs_renderer.rb

class TabsRenderer
  
  def initialize
(templateoptions={})
    @
template template
    
@options options
    
@tabs []
  end
  
  def create
(tab_idtab_textoptions={}, &block)
    
raise "Block needed for TabsRenderer#CREATE" unless block_given?
    @
tabs << [tab_idtab_textoptionsblock]
  end
  
  def render
    content_tag
(:div, (render_tabs render_bodies), {:id => :tabs}.merge(@options))
  
end
  
  private 
#  ---------------------------------------------------------------------------
  
  
def render_tabs
    content_tag 
:ul do
      @
tabs.collect do |tab|
        
content_tag(:lilink_to(content_tag(:spantab[1]), "##{tab[0]}") )
      
end
    end
  end
  
  def  render_bodies
    
@tabs.collect do |tab
      
content_tag(:divcapture(&tab[3]), tab[2].merge(:id => tab[0])) 
    
end.to_s
  end
  
  def method_missing
(*args, &block)
    @
template.send(*args, &block)
  
end
  
end

Now, include your tab assets and initialize your tabs as usual. TabsRenderer assumes that you want to ID your tab’s div as ‘tabs’.

<% javascript_include_tag("tabs/ui.tabs.pack.js") %>
<% 
stylesheet_link_tag("tabs/ui.tabs.css") %>

<
script type="text/javascript" charset="utf-8">
    $(function()
{
        
$('#tabs > ul').tabs();
    
});
</script>

Now use TabsRenderer to create some tabs ...notice how you can put conditions on the tail end of the block. We pass ‘self’ to our helper so that it knows where to delegate method calls that aren’t it’s own. Creating a tab is as simple as specifying a div #id and an Tab title.

<% tabs TabsRenderer.new(self) %>

    <% 
tabs.create('basic_tab''Basic Info') do %>
        
# ...
    
<% end %>

    <% 
tabs.create('gallery_tab''Gallery') do %>
        
# ...
    
<% end unless @images.blank? %>

    <% 
tabs.create('admin_tab''Admin') do %>
        <%= 
render :partial => "super_secret_stuff" %>
    <% 
end if current_user.has_role'admin' %>
    
<%= 
tabs.render %>

And here is the code that would be generated from the above example.

<div id="tabs">
    <
ul>
        <
li><a href="#basic_tab"><span>Basic Info</span></a></li>
        <
li><a href="#gallery_tab"><span>Gallery</span></a></li>
        <
li><a href="#admin_tab"><span>Admin</span></a></li>
    </
ul>
    <
div id="basic_tab">
        
# ... 
    
</div>
    <
div id="gallery_tab">
        
# ... 
    
</div>
    <
div id="admin_tab">
        
# ... rendered partial here
    
</div>
</
div>

Hope this is of use to someone else! Its sure been useful to me. I would love to hear your comments and criticisms. Please feel free to request changes to the class.

ps. Rails folks wanting to use JQuery in their projects should check out jrails, it wraps a lot of the prototype helpers.

pps. JQuery Rocks@!

3 Comments

Comment #1 by Dietrich  on  05/20  at  05:54 PM

Nice… gonna try this with some of my jquery/rails helpers…

You might want to add an options={} to pass styles for example:
def create(tab_id, tab_text, options={}, █)
@tabs << [tab_id, tab_text, options, block]
end

def render_bodies
@tabs.collect do |tab|
content_tag(:div, capture(&tab;[3]),tab[2].merge({:id => tab[0]}))

<% tabs = TabsRenderer.new(self) %>
<% tabs.create(’basic_tab’, ‘Basic Info’, {:style=>"background-color: #00FF00"}) do %>
# ...
<% end %>

Comment #2 by CodeOfficer  on  05/20  at  06:04 PM

oh perfect, I hadn’t thought of that. Thanks for the tip Dietrich .... I’ll add that this evening. =)

Comment #3 by CodeOfficer  on  05/20  at  09:17 PM

Added in your suggestion Dietrich smile

Leave a comment?

Name:

Email:

Location:

URL:

Remember my personal information

Notify me of follow-up comments?

Submit the word you see below:


RailsConf 2008 Often times I will release code for free or go that extra distance to help others online. If my skills were useful to you, please consider a small donation. Thank you very much.

recommend me!

Search

You Can Find Me

@ github.com
@ twitter.com

My Wishlists

@ Amazon.com

My Other Sites

Foundation's Edge, RJones Family, We're Not.com (only for staging), Ailee Jones (same as rjones for now)

Friends of Mine

Aaron, Barnaby, Brian, Chris, Dirk, Frank, Fred, Four, Justin, Matt, Mike, Monty, Paul, Sean, Travis

IRC Hangouts

I can usually be found lounging on irc.freenode.net while I work, on the following channels: #fauna, #github, #hello-heroku, #jquery, #passenger, #ruby, #rubyonrails, #slicehost, #sproutcore, #textmate, #werenot.