Creating a special UL LI menu and having issues with subpage

I'm having a little trouble with trying to get a UL menu working with rails. It's working somewhat, but not the way it needs to. I'll explain after I post the code.

First, I've created a page administration system similar to what was created in the screencasts for learningrails:

http://www.buildingwebapps.com/learningrails

What I like about the page administration system is having the ability to create pages on the fly without having to use a hard redirect to a controller. This is especially good for pages which are not static and always being changed. However, they use a standard menu system and do not showcase a dropdown menu system. I wanted to create a dropdown menu system instead.

The following code is in the application controller:

define get_pages_for_tabs   @tabs = Page.find_main   @subs = Page.find_sub end

In the page model the code does the following:

# this method defines the tab views for all tabs def self.find_main   Page.find(:all, :conditions => ['parent_id IS NULL'], :order => 'position') end

# this method defines the subpage tab views for all tabs def self.find_sub   Page.find(:all, :conditions => ['parent_id IS NOT NULL'], :order => 'position') end

What it's going to do simply put, is find all pages that do not have a parent_id set to them, which infers that those pages are -parent- pages. It sets those parent pages to @tabs.

It then finds all pages that do have parent_ids, which infers that those pages are -subpages- of the parent. It sets those pages to @subs, ordering them by position numbers set.

The following code below is listed in the application.html.erb file in my view layouts.

<ul id="menunav"> <% @tabs.each do |page| -%>   <% if page.redirect? %>     <li style="width: 90px;">       <%= link_to page.navlabel, {:action => page.action_name, :controller => page.controller_name, :name => page.name} -%>     </li>   <% else %>     <li style="width: 90px;">       <%= link_to page.navlabel, view_page_path(page.name) %>       <% @subs.each do |subpage| -%>         <% if page.id == subpage.parent_id %>           <ul>             <li><%= link_to subpage.navlabel, view_page_path(subpage.name) %></li>           </ul>         <% end %>       <% end %>     </li>   <% end %> <% end -%> </ul>

So, looking at the code above, here's what will happen:

If a page has a redirect box checked, it uses an MVC. If it doesn't, it uses a non-static database driven page instead.

The page model houses the following fields:

id, name, title, body, parent_id, navlabel, position, redirect, action_name, controller_name

If a page is going to be a sub-nav from a parent page menu item, I'll simply define the parent_id for that subpage which will match the parent.id.

The only problem with the code above is that it only displays one sub-menu for each parent menu item. If I have a parent page id of say 4 and I create three subpages that have parent_id = 4, it should look at the following code:

<% if page.id == subpage.parent_id %>

.. and showcase it.

It does so for only one subpage though.

What am I doing wrong based on the code shown? If you have any further questions about this, please let me know. Hopefully I provided all the information needed.

By the way, Merry Christmas everyone. :slight_smile:

Offering a visual example of what happens currently:

--Parent Menu Button I ----Fourth Sub Menu Button I

--Parent Menu Button II ----Fourth Sub Menu Button II

when what should happen is:

--Parent Menu Button I ----First Sub Menu Button I ----Second Sub Menu Button I ----Third Sub Menu Button I ----Fourth Sub Menu Button I

--Parent Menu Button II ----First Sub Menu Button II ----Second Sub Menu Button II ----Third Sub Menu Button II ----Fourth Sub Menu Button II

So, it's creating the menu buttons using UL Li styles but it's only placing the last instance of each array into the menu, versus adding all of them. When outputting this using puts in a rails console, it outputs everything properly. So, I'm not sure what's wrong with my code.

Any help would be appreciated.

I'm having a little trouble with trying to get a UL menu working with rails. It's working somewhat, but not the way it needs to. I'll explain after I post the code.

First, I've created a page administration system similar to what was created in the screencasts for learningrails:

Lost Redirection

What I like about the page administration system is having the ability to create pages on the fly without having to use a hard redirect to a controller. This is especially good for pages which are not static and always being changed. However, they use a standard menu system and do not showcase a dropdown menu system. I wanted to create a dropdown menu system instead.

The following code is in the application controller:

define get_pages_for_tabs @tabs = Page.find_main @subs = Page.find_sub end

First off, you'd be better off actually setting up some real associations. Such as:

class Page < ActiveRecord::Base

  belongs_to :parent, :class_name => 'Page'   has_many :sub_pages, :class_name => 'Page', :foreign_key => 'parent_id', :order => 'position'

  named_scope :main_pages, :conditions => 'pages.parent_id IS NULL', :order => 'pages.position', :include => :sub_pages

end

The controller code basically disappears; you can stash the results of Page.main_pages in an instance variable if you're hell-bent on avoiding model calls in the view.

Here's a cleaned-up version of the view code:

The only problem with the code above is that it only displays one sub-menu for each parent menu item. If I have a parent page id of say 4 and I create three subpages that have parent_id = 4, it should look at the following code:

<% if page.id == subpage.parent_id %>

.. and showcase it.

It does so for only one subpage though.

What am I doing wrong based on the code shown?

You didn't mention if the actual source from your original view includes all the elements; I'd hazard that the CSS for the menus might be dropping them all in a stack, which would produce the "only show the last one" behavior you've described.

Hope this helps!

--Matt Jones

Hi Matt,

Thanks a bunch for the helpful code. I didn't post my model associations but I did have all of the associations in place, like you suggested, with the exception of placing in a built-in order for position. So I added that and changed sub_pages to subpages throughout the code and also added in the scope which makes a lot of sense.

After testing the code, everything works as intended now. I'm going to spend some time mulling over what it is I just did thanks to your help, but I wanted to reply and let you know that it's all working fine now with your code changes and suggestions in place.

I appreciate it a great deal.

Take care mate.

Hi Matt and Everyone,

As always, I like to follow-up with detailed solutions and feedback on end topics I create.

I'm adding a link to gist which houses all of the changes and what works perfectly now:

http://gist.github.com/264456

In the application.html.erb layout, I don't particularly like having too much code, even code that pulls from the model. I like to move that into a helper file and since the menu resides within a table, I placed the real code in the table helper file.

In the application.html.erb layout, all you have to use is a simple call to the helper method of menu.

You'll see that I start with the call for @tabs in the application_controller. I mainly due this because I have administrative tabs that I don't want people to see, so I created a before_filter that looks for admin sessions and if so, admin tabs are displayed and if not, the public tabs are shown.

In the page.rb model, you'll see that I am now using the scope you created, but I'm also using another scope that applies to admin tabs.

Finally, in the table_helper.rb file, you'll see the code which controls two situations (those where true redirects are being used on the menu or those where virtual menu redirects are being used). However, it applies situational responses for both parent and sub-menu tabs.

I'm sure you'll understand it once you see it. And, I like to share code with folks that might have a similar issue they need solved.

As always, if you see any issues or you have ideas to clean up the resolved code, please post your response so others browsing later on, can find a cleaned up version of this topic.

Take care all.