views:

716

answers:

2

Imagine to have two RESTful controllers (UsersController, OffersController) and a PagesController (used for static content such as index, about and so on) in your application.

You have the following routes defined:

map.with_options :controller => 'pages' do |pages|
  pages.root :action => 'index'     # static home page
  pages.about :action => 'about'    # static about page
  # maybe more static pages...
end
map.resources :users    # RESTful UsersController
map.resources :posts    # RESTful PostsController

Your application layout looks like this:

<html>
  <head>
    <title>Demo Application</title>
  </head>
  <body>
    <ul id="menu">
       <li>
         <%= link_to 'Home', root_path %>
       </li>
       <li>
         <%= link_to 'Offers', offers_path %>
         <ul id="submenu>
           <li><%= link_to 'Search', 'path/to/search' %></li>
           <li>maybe more links...</li>
         </ul>
       </li>
       <li>
         <%= link_to 'About', about_path %>
       </li>
       <li>
         <%= link_to 'Admin', users_path %>
         <ul id="submenu">
           <li><%= link_to 'New User', new_user_path %></li>
           <li><%= link_to 'New Offer', new_offer_path %></li>
           <li>maybe more links</li>
         </ul>
       </li>
    </li>
    <%= yield %>
  </body>
</html>

The problem with the layout is that I want only one #submenu to be visible at any time. All other submenus can be completely skipped (don't need to rendered at all).

Take the Admin menu for example: This menu should be active for all RESTful paths in the application except for offers_path. Active means that the submenu is visible.

The only solution I can think of to achieve this is to build a lot complicated if conditions and that sucks (really complicated to write and to maintain). I'm looking for an elegant solution?

I hope someone understands my question - if there's something unclear just comment the question and I'm going to explain it in more detail.

+1  A: 

Typically, I would abstract out the menuing functionality so that I have a helper method for rendering the Admin menu. This way, it's possible to throw as much logic in the helper as you want without clouding up your view logic.

So, your helper would look like (forgive the ruby/rails pseudo-code, it's been a month or two since I touched it):

def render_admin_menu()
  if !current_path.contains('offer')
    render :partial => 'shared/admin_menu'
  end
end
Jeremiah Peschka
Thanks for your thoughts. However, moving the problem to a helper isn't a real solution. On the other hand, it's indeed an enhancement and I didn't expect to find an elegant solution any time soon anyway. Maybe someone else has a better trick...
Christoph Schiessl
+4  A: 

One thing you could play with is yield and content_for, used with a few partials for the menus. For example you could put each section of the menu in a partial and then modify your layout to something like:

<%= yield(:menu) %>

You then can then specify in your views content_for and put whatever partials you want into the menu. If it's not specified it won't get rendered.

<% content_for(:menu) do %>
   <%= render :partial => 'layouts/menu' %>
   <%= render :partial => 'layouts/search_menu' %>
    etc...
 <% end %>

If you're using a lot of the same menus in most pages, specify a default in your layout if no yield(:menu) is found.

<%= yield(:menu) || render :partial => 'layouts/menu_default' %>

Saves you a lot of typing. :) I've found this to be a nice clean way of handling things.

PJ