Skip to content

Instantly share code, notes, and snippets.

@maxivak
Last active April 8, 2024 01:03
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save maxivak/36b7b5254418ae3d9a93a71338143098 to your computer and use it in GitHub Desktop.
Save maxivak/36b7b5254418ae3d9a93a71338143098 to your computer and use it in GitHub Desktop.
Tree with ancestry. Rails

Contents:

  • show full path for the item
  • show tree in ol li
  • show tree in dropdown select

Show full path for item

  • one item

  • list of items

show full path for each item


# controller
@items = Category.where(..).all

# cache categories - all categories by ids
@all_categories_hash = Category.unscoped.all.inject({}){|res, elem| res[elem.id] = elem; res }


# view

- @items.each do |item|
    %h3= item.title
    path: 
    = item.ancestor_ids.map{|x| @all_categories_hash[x].title}.join(' / ')

Show tree for ancestry in Rails

Our model is organized in tree using ancestry gem.

We want to show in view the full tree with nested elements (with ul, li) like that:

<ul>
  <li>
        Fruits
        <ul>
          <li>Apple</li>
          <li>Orange</li>
        </ul>
  </li>
  <li>
        Vegetables
        <ul>
          <li>Tomato</li>
          <li>Potato</li>
          <li>Cabbage</li>
        </ul>
  </li>
</ul>
   
  • model
class Category ..

end

  • Category has attribute 'title' which will be shown in a tree.

  • controller


def index
  @items_tree = Category.all.arrange
end

  • create a helper method
# app/helpers/application_helper.rb

  def render_nested_groups(groups)
    s = content_tag(:ul) do
      groups.map do |group, sub_groups|
        content_tag(:li, (group.title +  nested_groups(sub_groups)).html_safe)
      end.join.html_safe
    end
  end

  • view

Use helper to present tree in view:

# app/views/categories/index.html.haml

= nested_groups(@items_tree)

Show tree in dropdown select

  • controller
  def edit
    @categories = collection_for_parent_select
  end
  

  def collection_for_parent_select
    @categories = ancestry_options(Category.unscoped.arrange(:order => 'name')) {|i| "#{'-' * i.depth} #{i.name}" }
  end

  def ancestry_options(items)
    result = []
    items.map do |item, sub_items|
      result << [yield(item), item.id]
      #this is a recursive call:
      result += ancestry_options(sub_items) {|i| "#{'-' * i.depth} #{i.name}" }
    end
    result
  end

  • form
= f.input :parent_id, :as => :select, :collection => @categories
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment