Here’s an example of how to use ActsAsTree to display a list of categories. I’ve seen other ones done that display only subcategories or display all the categories AND the subcategories so that the subs look like roots of the tree.. very ugly. I’ve made this code so that it will list ONLY roots at the top and then children so it looks just like a tree should look. Your roots will have to have parent_id equal to 0.
First, slap this in your view:
<pre><%= display_categories(@categories) %></pre>
This is the heart of the code. You’ll want this in a helper – most likely application helper because you’ll want to pull this from multiple views:
<pre>
def display_categories(categories)
ret = "<ul>"
for category in categories
if category.parent_id == 0
ret += "<li>"
ret += link_to category.name
ret += find_all_subcategories(category)
ret += "
"
end
end
ret += "
"
end
def find_all_subcategories(category)
if category.children.size > 0
ret = ‘
I’ve managed to break the DRY rule in this. If anyone has any suggestions for a better way around this I’d love to hear it, so please comment.
So lets refine this code to get DRY again :-)
Warning this is unchecked code.
07-05-18 bugfixes for the first 2 comments.
The first thing to do seems to avoid the repetition for the children.size > 0 case. And to get a bit more rubyist ;-)
<pre>
def find_all_subcategories(category)
ret = ''
if category.children.any?
ret << '<ul>'
category.children.each do |subcat|
ret << '<li>'
ret << link_to h(subcat.name), :action => 'edit', :id => subcat
if subcat.children.any?
ret << find_all_subcategories(subcat)
end
ret << '
’
end
ret << ’
’
end
ret
end
Prettier isn’t it ?
We see that we have the same code for each category line :
<pre>
def display_categories(categories)
..
ret += "<li>"
ret += link_to category.name
ret += find_all_subcategories(category)
ret += "
"
..
end
and
<pre>
def find_all_subcategories(category)
..
ret << '<li>'
ret << link_to h(subcat.name), :action => 'edit', :id => subcat
if subcat.children.any?
ret << find_all_subcategories(subcat)
end
ret << '
’
..
end
Ok, well, not exactly the same code.
So, let’s apply these changes :
<pre>
def display_categories(categories)
ret = "<ul>"
for category in categories
if category.parent_id.nil?
ret << "<li>"
ret << link_to h(category.name), :id => category
ret << find_all_subcategories(category) if category.children.any?
ret << "
"
end
end
ret << "
"
end
def find_all_subcategories(category)
ret = ‘
Ok, now that the code is exactly the same, we can extract it to a method :
<pre>
def display_categories(categories)
ret = "<ul>"
for category in categories
if category.parent_id.nil?
ret << display_category(category)
end
end
ret << "
"
end
def find_all_subcategories(category)
ret = ‘
I think the parent.nil? is useless in display_categories (because you want a display_categories(Category.find_by_parent_id(nil))). This allows to merge display_categories and find_all_subcategories :
<pre>
def display_categories(categories)
ret = "<ul>"
for category in categories
ret << display_category(category)
end
ret << "
"
end
Much shorter isn’t it? :-)
I tried revision 3 but Categories would be displayed in the root even if their parent_id’s weren’t 0. Therefore, I just changed the code a bit. One change was in the application_helper.rb and the other in the view.
Application Helper:
<pre>
def display_categories(categories, parent_id)
ret = "<ul>"
for category in categories
if category.parent_id == parent_id
ret << display_category(category)
end
end
ret << "
"
end
The View:
<pre>
<%= display_categories(@categories, 0) %>
</pre>
Your code doesn’t work for me: if the root has parent_id == 0, Category.root returns nil.
So i left root.parent_id == NULL and modified the helper code in this way:
<pre>
def display_categories(categories, parent_id)
ret = "<ul>"
for category in categories
if category.parent_id == nil
category.parent_id = 0
if category.parent_id == parent_id
ret << display_category(category)
end
end
ret << "
"
end
Here’s an example of how to use ActsAsTree to display a list of categories. I’ve seen other ones done that display only subcategories or display all the categories AND the subcategories so that the subs look like roots of the tree.. very ugly. I’ve made this code so that it will list ONLY roots at the top and then children so it looks just like a tree should look. Your roots will have to have parent_id equal to 0.
First, slap this in your view:
<pre><%= display_categories(@categories) %></pre>
This is the heart of the code. You’ll want this in a helper – most likely application helper because you’ll want to pull this from multiple views:
<pre>
def display_categories(categories)
ret = "<ul>"
for category in categories
if category.parent_id == 0
ret += "<li>"
ret += link_to category.name
ret += find_all_subcategories(category)
ret += "
"
end
end
ret += "
"
end
def find_all_subcategories(category)
if category.children.size > 0
ret = ‘
I’ve managed to break the DRY rule in this. If anyone has any suggestions for a better way around this I’d love to hear it, so please comment.
So lets refine this code to get DRY again :-)
Warning this is unchecked code.
07-05-18 bugfixes for the first 2 comments.
The first thing to do seems to avoid the repetition for the children.size > 0 case. And to get a bit more rubyist ;-)
<pre>
def find_all_subcategories(category)
ret = ''
if category.children.any?
ret << '<ul>'
category.children.each do |subcat|
ret << '<li>'
ret << link_to h(subcat.name), :action => 'edit', :id => subcat
if subcat.children.any?
ret << find_all_subcategories(subcat)
end
ret << '
’
end
ret << ’
’
end
ret
end
Prettier isn’t it ?
We see that we have the same code for each category line :
<pre>
def display_categories(categories)
..
ret += "<li>"
ret += link_to category.name
ret += find_all_subcategories(category)
ret += "
"
..
end
and
<pre>
def find_all_subcategories(category)
..
ret << '<li>'
ret << link_to h(subcat.name), :action => 'edit', :id => subcat
if subcat.children.any?
ret << find_all_subcategories(subcat)
end
ret << '
’
..
end
Ok, well, not exactly the same code.
So, let’s apply these changes :
<pre>
def display_categories(categories)
ret = "<ul>"
for category in categories
if category.parent_id.nil?
ret << "<li>"
ret << link_to h(category.name), :id => category
ret << find_all_subcategories(category) if category.children.any?
ret << "
"
end
end
ret << "
"
end
def find_all_subcategories(category)
ret = ‘
Ok, now that the code is exactly the same, we can extract it to a method :
<pre>
def display_categories(categories)
ret = "<ul>"
for category in categories
if category.parent_id.nil?
ret << display_category(category)
end
end
ret << "
"
end
def find_all_subcategories(category)
ret = ‘
I think the parent.nil? is useless in display_categories (because you want a display_categories(Category.find_by_parent_id(nil))). This allows to merge display_categories and find_all_subcategories :
<pre>
def display_categories(categories)
ret = "<ul>"
for category in categories
ret << display_category(category)
end
ret << "
"
end
Much shorter isn’t it? :-)
I tried revision 3 but Categories would be displayed in the root even if their parent_id’s weren’t 0. Therefore, I just changed the code a bit. One change was in the application_helper.rb and the other in the view.
Application Helper:
<pre>
def display_categories(categories, parent_id)
ret = "<ul>"
for category in categories
if category.parent_id == parent_id
ret << display_category(category)
end
end
ret << "
"
end
The View:
<pre>
<%= display_categories(@categories, 0) %>
</pre>
Your code doesn’t work for me: if the root has parent_id == 0, Category.root returns nil.
So i left root.parent_id == NULL and modified the helper code in this way:
<pre>
def display_categories(categories, parent_id)
ret = "<ul>"
for category in categories
if category.parent_id == nil
category.parent_id = 0
if category.parent_id == parent_id
ret << display_category(category)
end
end
ret << "
"
end