Topic: acts as tree simple menu

This is my first very simple tutorial how to make simple menu using acts as tree.
Database table must have id, name and parent_id columns.

#category model
acts_as_tree :order => "name"

#controller
@categories = Category.find:all

#view
<ul>
  <% for category in Category.find_all_by_parent_id(nil) %>
  <li>
  <%= link_to h(category.name), :controller => 'categories', :action => 'show', :id => category.id %>
    <% if Category.find_all_by_parent_id(category.id).empty? %>
    <span>no subcategories</span>
    <% else %>
      <ul><% for category in Category.find_all_by_parent_id(category.id) %>
        <li>
        <%= link_to h(category.name), :controller => 'categories', :action => 'show', :id => category.id %>
        </li>
        <% end %>
      </ul>
    <% end %>
  </li>
  <% end %>
</ul>

And thats all. Maybe it will be usefull for someone.

Last edited by zattrix (2006-12-05 19:33:54)

Re: acts as tree simple menu

Good tutorial. Just a couple suggestions. Instead of using find_all_by_parent_id, you can do category.children. Also, to allow unlimited depth, you can use partials. The end result looks like this:

<ul><%= render :partial => 'category', :collection => Category.find_all_by_parent_id(nil) %></ul>

# in _category.rhtml partial
<li>
  <%= link_to h(category.name), :controller => 'categories', :action => 'show', :id => category.id %>
  <% if category.children.empty? %>
    <span>no subcategories</span>
  <% else %>
    <ul><%= render :partial => 'category', :collection => category.children %></ul>
  <% end %> 
</li>

Railscasts - Free Ruby on Rails Screencasts

Re: acts as tree simple menu

Thank you for your suggestions.
I tried to do the tree this way but it didn't work :-(. Your way is really much better.

Re: acts as tree simple menu

Hmm, what's not working? Are you getting an error?

Railscasts - Free Ruby on Rails Screencasts

Re: acts as tree simple menu

Your code is ok. It's working well.
But when i tried to do the same as you, before it didnt worked. Of course it wasn't the same, but i tried to use category.children to do the tree and it wasn't a successful attempt for me smile. So i do that like in the first post smile

Re: acts as tree simple menu

Interesting tutorial.  I'm a newbie at rails though, and i can't get this to work.  I'm not getting any errors, just an empty unordend list in my page. I'm confused as to what code should go where, that's probably what i'm doing wrong.
It'd be great if you could write out in more detail which controllers and models to generate, and what to put in them.

Re: acts as tree simple menu

I'm assuming you have populated the database? Are there some categories with no parent_id so they are properly listed at the root?

Railscasts - Free Ruby on Rails Screencasts

Re: acts as tree simple menu

Ah, i just gave 'em a parent_id of 0.  That seems to have been the problem.
Thanks ryanb; you are a genius.

Re: acts as tree simple menu

Thanks for this. I was struggling for quite a while with some of the other postings on this. I actually got yours working first time. Now I'm going to customise it to my needs.

Re: acts as tree simple menu

how can u hide those subcategories, and toggle them for display, for each link generated...

im using .toggle(  ), but i am only able to update a specific <ul></ul> for a specific category.

so what i mean is, when i click on a category, it will display those subcategories without reloading the page. and this should work for all category links, except that i dont know how to NOT make it toggle just one element. errr really confusing.


this would allow, easy navigation.

Last edited by jjk2 (2007-05-04 05:30:34)

Re: acts as tree simple menu

You need to give each ul tag a unique "id" attribute. You can do this by using the id column of your category model.

<li>
  <%= link_to h(category.name), :controller => 'categories', :action => 'show', :id => category.id %>
  <% if category.children.empty? %>
    <span>no subcategories</span>
  <% else %>
    <ul id="category_children_<%= category.id %>">
      <%= render :partial => 'category', :collection => category.children %>
    </ul>
  <% end %> 
</li>

Then it's a matter of hiding and showing that element when clicking a link.

<%= link_to_function 'toggle children', "Element.toggle('category_children_#{category.id}');" %>

Or something like that.

Railscasts - Free Ruby on Rails Screencasts

Re: acts as tree simple menu

ah i put hte #{category.id} in the wrong place i see. I originally placed that in the id="" of the ul element.

thank you.

Re: acts as tree simple menu

Hi

First of all, I'm new here so 'hi'! And as the name suggests, I'm also very new to RoR.

I'm trying to get this to work but it's not outputting anything to the screen. 

Here's what I have so far:

DB (categories):

+-----+---------+-----------+
| id  | name    | parent_id |
+-----+---------+-----------+
| 135 | Animals | 0         |
| 136 | Cats    | 135       |
| 134 | kittens | 136       |
+-----+---------+-----------+

Model (category.rb):
class Category < ActiveRecord::Base
  acts_as_tree :order => "name"
  belongs_to :sections 
end

Controller (categories_controller.rb):
  def list
    @categories = Category.find:all
  end

View (list.rhtml):
<ul>
    <%= render :partial => 'category', :collection => Category.find_all_by_parent_id(nil) %>
</ul>

Partial (_category.rhtml):
<li>
<%= link_to h(category.name), :controller => 'categories', :action => 'show', :id => category.id %>
<% if category.children.empty? %>
    <span>no subcategories</span>
<% else %>
    <ul><%= render :partial => 'category', :collection => category.children %></ul>
<% end %> 
</li>

Is there anything wrong with what I have done?

Thanks,
Eric

Re: acts as tree simple menu

The problem is that it's looking for a category with parent_id of NULL (nil), but you just have one with the parent_id of 0. You should either change the database or change the search.

Railscasts - Free Ruby on Rails Screencasts

Re: acts as tree simple menu

Thanks!  that fixed it.

btw, i really enjoy your podcasts!

Re: acts as tree simple menu

Interesting tutorial.  I learn more about Acts As Tree.

Regarding the "toggle children" function, pls enlighten me how to write the code to allow toggling of the children records.

Thank you.

Re: acts as tree simple menu

Using the "collection" does not display the parent.  Is there a way to do this?

I have 2 models: Group and User.
- Group has these fields:  id, name, parent_id.
- User has these fields:   id, name, group_id.

what I need is:
1.  User to enter the (parent) group that s/he wants to see.
2.  I need to display the (parent) group as entered by user, then the children groups, and lastly the users under the (parent) group.

Thank you very much for your help.

Re: acts as tree simple menu

If you only need to display one layer of children you may not need to do anything complicated with partials.

<%= @parent_group.name %>
<% for group in @parent_group.children %>
  <%= group.name %>
<% end %>
<% for user in @parent_group.users %>
  <%= user.name %>
<% end %>

Railscasts - Free Ruby on Rails Screencasts

Re: acts as tree simple menu

Ryan,

Thank you very much for your reply.

Question 1:
-----------
Example of my scenario is as follows:


                                               Group-1
                                                     |
                ------------------------------------------------------------------------------
                |                           |              |            ..   |                           ..   |      ..|
          Group-2                       Group-3         Group-4                                                                     Group-9
                                            |                                       |
                 -------------------------------------------------------           User-9   
                 |            |             |            |             |                 
           Group-3.1      Group-3.2     Group-3.3     User-3.1     User-3.2         
                 |
        ----------------
        |              |
Group-3.1.1       Group-3.1.2

Note: 
1.  Group can have sub-groups.  I have Group model with parent_id.
2.  User is under a group.  I have User model with group_id.
3.  Group/sub-group can have zero or more users.
4.  The tree above shows that user cannot have sub-group under it.
5.  I have multiple layers of groups and sub-groups.

How can I display these 2 models - Group and User in the same tree structure.


Question 2:
-----------
Based on the partial code in the earlier post, I tried the following.  But, I encountered error saying Nil is encountered in the 2nd line of _group.html:

Group controller:

def list
  @parent_id = ?? # where ?? = id set by other part of the controller
  @group = Category.find_all_by_parent_id(@parent_id)
  if @group.empty?
    #... display NO SEARCH RESULTS
  end
end

list.rhtml:

<%= render :partial => 'group', :collection => @group %>

_group.rhtml:

As per the partial code in the earlier post, where the 2nd line reads as:
  <%= link_to h(group.name), :controller => 'group', :action => 'show', :id => group.id %>

Re: acts as tree simple menu

The recursive partial works great for me. I am ordering my tree using an integer column named position. In my list I want to have an Up/Down command to let me reorder children. So I added up/down links which work. But now I only want to show up/down links when they make sense: i.e., the top child will not show the up link and the bottom child won't show the down link.

The top child is easy to do, as I just check item.position == 0. However, the bottom one is trickier. Currently I run a query to check if the position of the item is equal to the max position for that set of children.

I'm hopeful that there's some nice Ruby/Rails feature like item.last? that would work for me. If I could access the collection that gets looped in the partial, I could do something like item == collection.last, but I don't know how to access the collection from the partial.

Any suggestions?

Last edited by boone (2007-06-24 17:11:36)