So, a while back I wrote a quick widget for making sliding door tabs. But it wasn't enough, I wanted *MORE*. I wanted them to be responsive to conditions.
For example, I have a resource User, and I want the "users" tab to stay open no matter which user page I'm on (index, show, edit etc)... *unless*, of course, it happens also to be the "my account" page (ie I'm viewing myself)... for which I already have a tab. This is a cut above just being "on" if we happen to perfectly match the current url.
So I rewrote the helper methods. Now my tabs come with optional separators (that can be independantly styled), and with the ability to pass in a conditional to override whether or not to show the tab as "on".
def make_tab(t) # use the conditional if one was passed - default to current page cond = t.has_key?(:cond) ? t[:cond] : current_page?(t[:options]) content_tag "li", link_to(t[:name], url_for(t[:options])), :class => (cond ? 'current' : nil) end # Builds a tab that is just a separator def make_separator "<li class='separator'></li>" end # Make tablist out of a given set of tabs. def build_tablist(tabs, do_sep = true) list = "<ul>" tabs.each_with_index do |t,i| list << make_tab(t) list << make_separator if do_sep && i != tabs.size - 1 end list << "</ul>" list end
And an example of use (that assumes existance of restful_auth)
<div id="topnav" class="tabNav"> <% # standard set of tabs tabs = [] if logged_in? tabs << {:name => 'dashboard', :options => dashboard_url} tabs << {:name => "my account", :options => user_path(current_user)} if current_user.is_admin? tabs << {:name => "widgets", :options => widgets_path, :cond => (current_controller?(hash_for_widgets_path))} tabs << {:name => "users", :options => users_path, :cond => (current_controller?(hash_for_users_path) && !current_page?(user_path(current_user)))} end tabs << {:name => 'logout', :options => logout_url} else tabs << {:name => 'login', :options => login_url} tabs << {:name => 'forgot password', :options => forgot_password_url} end -%> <!-- tabbed browsing of main site areas --> <%= build_tablist tabs, false -%> </div>
Note that it also uses a convenience function: "current_controller?" This is below and could almost certainly be done better... but it currently serves to check if the given url-options match the current controller.
# determines if the given set of options contains the current controller def current_controller?(options) return true if current_page?(options) # trivial case if options.respond_to?(:has_key?) && options.has_key?(:controller) return options[:controller] == controller.controller_name end # try matching it - assumes it's a string url options =~ /#{controller.controller_name}/ end
4 comments:
Thanks for showing how to do tabbed navigation in Rails so efficiently.
I was using tabnav plugin but it is way too complex and I couldn't get it to work with ajax / link_to_remote. I was able to copy your code and modify it successfully to use link_to_remote. I now intend replacing all my tabnavs with variants of your helpers.
Thanks - glad it's useful for you!
Oh, and would you be willing to share your tips on converting for use with link_to_remote? :)
Update from "anonymous".
What I have is a set of navigation tabs and in each of these tabs I typically have a paginated table. Ideally I want both the tabs and the paginated output to be ajax enabled such that clicking on a tab just refreshes the tab content on the screen and clicking on the pagination controls just displays the next page inside the selected tab - a not unreasonable or uncommon pattern (imho).
I was using the tabnav plugin (http://www.seesaw.it/en/toolbox/widgets/) and will_paginate (http://rock.errtheblog.com/will_paginate).
I came across several approaches to making will_paginate ajax enabled (e.g. http://railsontherun.com/2007/9/27/ajax-pagination-in-less-than-5-minutes). I found the best solution to be at Redline (http://weblog.redlinesoftware.com/2008/1/30/willpaginate-and-remote-links).
But I could not get tabnav ajax enabled (probably due to my lack of understanding of the plugin). So I copied and modified your code samples as they were more comprehensible.
Code extracts are as follows:
*** rhtml ***
:
:
< div id="topnav" class="tabNav">
< % tabs = [
{ :name => 'Documents',
:options => {:url => {:action => "render_tabs", :tab => 'documents', :id => person},
:update => 'person_tab',
:before => "Element.show('spinner')",
:complete => "Element.hide('spinner')"}},
{ :name => 'Tasks',
:options => {:url => {:action => "render_tabs", :tab => 'tasks', :id => person},
:update => 'person_tab',
:before => "Element.show('spinner')",
:complete => "Element.hide('spinner')"}}
] %>
< %= build_tablist tabs, false % >
< /div>
< br />< br />
< div id="person_tab" >< /div>
:
:
*** tab helper ***
module TabsHelper
def make_tab(tab)
"< li>" + link_to_remote(tab[:name], tab[:options]) +"< /li>"
end
:
:
*** controller ***
:
:
def render_tabs
:
render :partial => "/documents/list", :locals => {:documents => @authored_documents}
:
end
:
:
Post a Comment