You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails 7+Stimulus:动态隐藏无数据的子子分类下拉菜单

问题:三级分类联动下拉菜单按需显示子子分类

我实现了分类/子分类/子子分类三级联动下拉菜单,整体功能正常,但存在一处问题:部分子分类没有对应的子子分类,我希望隐藏最后一个子子分类下拉菜单,仅当用户选择的子分类存在子子分类时才显示它。恳请提供解决方案。


现有代码

数据库表结构(categories)

create_table :categories do |t|
  t.string   "name",        limit: 255
  t.text     "description", limit: 65535
  t.integer  "parent_id",   limit: 4
  t.boolean  "important",                 default: false
  t.integer  "position",    limit: 4,     default: 0
  t.timestamps
end

Category 模型(category.rb)

class Category < ApplicationRecord
  validates :name, presence: true
  has_many :jobs
  belongs_to :parent, foreign_key: :parent_id, class_name: 'Category' , :optional => true
  has_many :subcategories, foreign_key: :parent_id, class_name: 'Category'
  has_many :subsubcategories, foreign_key: :parent_id, class_name: 'Category'
end

Job 模型(job.rb)

class Job < ApplicationRecord
  validates :title,:category_id, :description, presence: true
  validates :category, :presence => true

  belongs_to :user
  belongs_to :category , -> { order("name") }
  belongs_to :subcategories, class_name: "Category"
  belongs_to :subsubcategories, class_name: "Category"
end

Stimulus 控制器(dropdown_controller.js)

import { Controller } from "@hotwired/stimulus";

// connect to data-controller="dropdown"
export default class extends Controller {
    submit () {
           this.element.requestSubmit();
    }
}

Jobs 控制器(jobs_controller.rb)

class JobsController < ApplicationController
  before_action :authenticate_user!, except: [:index, :show]
  before_action :set_categories

  def index
    @jobs = Job.all
  end

  def new
    @category = Category.find_by(id: params[:category])
    @categories = Category.where(parent_id: nil)  
    @subcategories = @category.subcategories if @category
    @subsubcategories = Category.where(:parent_id => params[:subcategories])  
    @job = Job.new
  end

  def create
    @job = Job.new(job_params.merge({ user: current_user }))
    if @job.save
      format.html { redirect_to root_path, notice: "Job was successfully created." }
    else
      render :new, status: :unprocessable_entity
    end
  end

private
  def job_params
    params.require(:job).permit(:title, :description,:category_id).merge(user: current_user)
  end

  def set_categories
    @category = Category.find_by(id: params[:category].presence)
    @subcategory = Category.find_by(id: params[:subcategories])
    @subsubcategory = Category.find_by(id: params[:subsubcategories])
  end
end

视图文件(views/jobs/new.html.erb)

<%= turbo_frame_tag "form" do %>
  <%= form_tag new_job_path, method: :get, data: { controller: "dropdown", action: "change->dropdown#submit" } do %>
    <%= select_tag  :category, options_from_collection_for_select(@categories, "id", "name", @category&.id ), prompt:  "Select a category" %>
    <%= select_tag  :subcategories, options_from_collection_for_select(@subcategories  || [], "id" , "name", @subcategory&.id), prompt:  "Select a subcategories category" %>
    <%= select_tag  :subsubcategories, options_from_collection_for_select(@subsubcategories || [], "id" , "name", @subsubcategory&.id), prompt:  "Select a subsubcategories category" %>
  <% end %>
<% end %>

解决方案

1. 控制器层:添加子子分类存在性判断

修改 jobs_controller.rb 的 new 方法,新增变量判断选中的子分类是否有子子分类,仅存在时加载数据:

def new
  @category = Category.find_by(id: params[:category])
  @categories = Category.where(parent_id: nil)  
  @subcategories = @category.subcategories if @category
  
  # 获取当前选中的子分类
  @selected_subcategory = Category.find_by(id: params[:subcategories])
  # 标记是否存在子子分类
  @has_subsubcategories = @selected_subcategory&.subsubcategories&.exists?
  # 仅当存在时加载子子分类数据
  @subsubcategories = @selected_subcategory.subsubcategories if @has_subsubcategories
  
  @job = Job.new
end

2. 视图层:条件渲染子子分类下拉框

修改 views/jobs/new.html.erb,只有在 @has_subsubcategories 为真时才显示最后一个下拉框:

<%= turbo_frame_tag "form" do %>
  <%= form_tag new_job_path, method: :get, data: { controller: "dropdown", action: "change->dropdown#submit" } do %>
    <%= select_tag  :category, options_from_collection_for_select(@categories, "id", "name", @category&.id ), prompt:  "选择分类" %>
    <%= select_tag  :subcategories, options_from_collection_for_select(@subcategories  || [], "id" , "name", @subcategory&.id), prompt:  "选择子分类" %>
    
    <%# 仅当前子分类有子子分类时显示 %>
    <% if @has_subsubcategories %>
      <%= select_tag  :subsubcategories, options_from_collection_for_select(@subsubcategories || [], "id" , "name", @subsubcategory&.id), prompt:  "选择子子分类" %>
    <% end %>
  <% end %>
<% end %>

可选优化:简化模型关联

Category 模型中 subcategories 和 subsubcategories 逻辑重复,可以合并为通用关联,方便后续层级扩展:

class Category < ApplicationRecord
  validates :name, presence: true
  has_many :jobs
  belongs_to :parent, foreign_key: :parent_id, class_name: 'Category', optional: true
  # 通用子分类关联
  has_many :children, foreign_key: :parent_id, class_name: 'Category'
  # 保留原有别名兼容现有代码
  has_many :subcategories, class_name: 'Category', foreign_key: :parent_id
  has_many :subsubcategories, through: :subcategories, source: :children
end

内容的提问来源于stack exchange,提问作者ukeagle

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 20:42:04