首页 > 解决方案 > rails 6中的多个下拉菜单

问题描述

对 Rails 很陌生,不知道如何构建动态菜单栏。数据库使用一种简单的方法来存储菜单项:

id | title   | url             | parent | position
--------------------------------------------------------------------
1    Books     /books            0        1 (first link on menu bar)
2    Fiction   /books/fiction    1        1 (first link under Books menu)
3    CDs       /cd               0        2 (second link on menu bar)
4    Fantasy   /books/fantasy    1
5    Singles   /cd/single        2
6    Albums    /cd/album         2

我需要获取此表中的所有条目并根据其父项显示它们。父级为 0 的所有项目都将显示在菜单栏上。当用户悬停任何父级时,其所有子元素都列在其下方。

当我在 PHP 中执行此操作时,我使用了一个导致许多数据库调用的循环;一种是按位置顺序获取所有父项(因为管理员可以更改父项在菜单上出现的顺序),然​​后是每个父项以获取其所有子元素(也是按位置顺序)。然后,我将每个结果项添加到以 parent_id 命名的数组中,最后连接数组。

$item[$parent][] = '<a href...'

我知道 SQL 效率低下(PHP 可能也是如此),但当时这并不重要。现在确实如此。

知道如何在 ruby​​ 中有效地完成这项工作吗?

标签: mysqlruby-on-railsdrop-down-menumemory-efficient

解决方案


假设存储此菜单信息的数据库表名为 nav_items。然后 Rails 约定将有一个名为 NavItem 的模型:

# app/models/nav_item.rb
class NavItem < ApplicationRecord
  has_many :child_menu_items, class_name: NavItem, inverse_of: :parent_menu_item, foreign_key: :parent
  belongs_to :parent_menu_item, class_name: NavItem, foreign_key: :parent, inverse_of: :child_menu_items

  scope :top_level, { where("parent = ?", 0) }
end

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  before_action :populate_nav_vars

  private
  def populate_nav_vars
    @nav_items = NavItem.top_level
  end
end

# app/view/layouts/_nav.html
# @nav_items is defined in the controller as NavItem.top_level
# the nav structure is shown here as nested <ul> lists
# it can be rendered appropriately with css (e.g. Bootstrap)
<ul>
<%- @nav_items.each do |top_level_item| %>
   <li><%= top_level_item.title %></li>
   <li>
     <ul>
       <%- top_level_item.child_menu_items.each do |child_item| %>
         <li><%= child_item.title %>
       <%- end %>
     </ul>
   </li>
<%- end %>
</ul>

你真的不应该担心性能,它根本不应该被注意到。我在这里假设您希望每个页面上都有导航菜单,因此填充所需变量的方便位置是在ApplicationControllerbefore_action 回调中填充变量。

如果性能问题与您有关,那么您应该将导航页面元素设置为部分视图,并将 rails 缓存应用于部分视图。


推荐阅读