如何将嵌套集中的所有记录呈现为真正的html树

我在我的Rails项目中使用了awesome_nested_set插件。 我有两个看起来像这样的模型(简化):

 class Customer < ActiveRecord::Base has_many :categories end class Category  :customer_id validates_presence_of :name # Further validations... end 

数据库中的树按预期构造。 parent_idlftrgt所有值都是正确的。 树有多个根节点(当然在awesome_nested_set允许)。

现在,我想在一个正确排序的树中呈现给定客户的所有类别,例如结构:例如嵌套的

    标签。 这不会太难,但我需要它才能有效(sql查询越少越好)。

    更新:计算出可以在没有进一步SQL查询的情况下计算树中任何给定节点的子节点数: number_of_children = (node.rgt - node.lft - 1)/2 。 这并不能解决问题,但可能会有所帮助。

    如果嵌套集具有开箱即用的更好的function将是不错的。

    您发现的技巧是从平面集构建树:

    • 以一组按lft排序的所有节点开始
    • 第一个节点是根添加它,因为树的根移动到下一个节点
    • 如果它是前一个节点的子节点(prev.lft和prev.rht之间的lft)将一个子节点添加到树中并向前移动一个节点
    • 否则将树向上移动一级并重复测试

    见下文:

     def tree_from_set(set) #set must be in order buf = START_TAG(set[0]) stack = [] stack.push set[0] set[1..-1].each do |node| if stack.last.lft < node.lft < stack.last.rgt if node.leaf? #(node.rgt - node.lft == 1) buf << NODE_TAG(node) else buf << START_TAG(node) stack.push(node) end else# buf << END_TAG stack.pop retry end end buf <

    #{node.name}

      " end def NODE_TAG(node) "
    • #{node.name}

    • " end def END_TAG "
    " end

    我最近回答了类似的php问题 (嵌套set == modified preorder tree traversal model)。

    基本概念是通过一个SQL查询获取已经排序的节点和深度指示器。 从那里它只是通过循环或递归渲染输出的问题,所以应该很容易将其转换为ruby。

    我不熟悉awesome_nested_set插件,但它可能已经包含一个获取深度注释,有序结果的选项,因为在处理嵌套集时它是一个非常标准的操作/需要。

    自2009年9月以来,令人敬畏的嵌套集包括一个特殊的方法来执行此操作: https : //github.com/collectiveidea/awesome_nested_set/commit/9fcaaff3d6b351b11c4b40dc1f3e37f33d0a8cbe

    此方法比调用级别更有效,因为它不需要任何其他数据库查询。

    示例:Category.each_with_level(Category.root.self_and_descendants)do | o,level |

    您必须以递归方式呈现将调用自身的部分。 像这样的东西:

     # customers/show.html.erb 

    Name: <%= @customer.name %>

    Categories

      <%= render :partial => @customer.categories %>
    # categories/_category.html.erb
  • <%= link_to category.name, category %>
      <%= render :partial => category.children %>
  • 这是Rails 2.3代码。 在此之前,您必须调用路由并明确命名部分。

    _tree.html.eb

     @set = Category.root.self_and_descendants <%= render :partial => 'item', :object => @set[0] %> 

    _item.html.erb

     <% @set.shift %> 
  • <%= item.name %> <% unless item.leaf? %>
      <%= render :partial => 'item', :collection => @set.select{|i| i.parent_id == item.id} %>
    <% end %>
  • 你也可以对他们进行排序:

      <%= render :partial => 'item', :collection => @set.select{|i| i.parent_id == item.id}.sort_by(&:name) %> 

    但在这种情况下你应该删除这一行:

     <% @set.shift %> 

    我想,因为我写的旧版ruby,我无法完成接受的答案。 这是适合我的解决方案:

     def tree_from_set(set) buf = '' depth = -1 set.each do |node| if node.depth > depth buf << "
    • #{node.title}" else buf << "
    " * (depth - node.depth) buf << "
  • #{node.title}" end depth = node.depth end buf << "
  • " * (depth + 1) buf.html_safe end

    通过使用可选的深度信息简化了它。 (这种方法的优点是输入集不需要是叶子的整个结构。)

    没有深度的更复杂的解决方案可以在gem的github wiki上找到:

    https://github.com/collectiveidea/awesome_nested_set/wiki/How-to-generate-nested-unordered-list-tags-with-one-DB-hit

    也许有点晚了但我想基于closure_tree gem嵌套的hash_tree方法分享我对awesome_nested_set的解决方案:

     def build_hash_tree(tree_scope) tree = ActiveSupport::OrderedHash.new id_to_hash = {} tree_scope.each do |ea| h = id_to_hash[ea.id] = ActiveSupport::OrderedHash.new (id_to_hash[ea.parent_id] || tree)[ea] = h end tree end 

    这适用于lft订购的任何范围

    比使用帮助器渲染它:

     def render_hash_tree(tree) content_tag :ul do tree.each_pair do |node, children| content = node.name content += render_hash_tree(children) if children.any? concat content_tag(:li, content.html_safe) end end end