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:

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

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:

http://gist.github.com/264005

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.