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

如何在Rails中实现基于模型的联动嵌套下拉菜单

问题原因

你当前的写法错误是因为Site.where(study_id: :id)是ERB在服务端渲染页面阶段就执行的代码,此时用户还未在前端页面操作第一个下拉框,:id只是一个无意义的符号值,根本获取不到用户后续选中的动态ID,所以无法实现联动过滤。

推荐实现方案

根据你的数据量大小,可以选择以下两种方案:

方案1:AJAX异步加载(数据量较大时推荐)

不需要提前加载全量站点数据,选中研究后动态请求对应站点列表,性能更优。

  1. 修改ERB模板,给两个下拉框增加唯一ID,第二个下拉框先留空占位:
<div class="study-search">
  <%= form_with(url: show_subjects_path(:site_id), method: :get, html: {style: 'font-size:16px'}) do |f| %>
    <%= f.label 'Select Study' %>
    <%= f.select :id, Study.all.collect {|study| [study.title, study.id] },  {:include_blank => "--Choose--"}, id: "study-select" %>
    <%= f.label 'Select Site' %>
    <%= f.select :site_id, [],  {:include_blank => "--Choose--"}, id: "site-select" %>
    <div class="btn btn-sm" style="display: inline">
      <%= f.submit "GO" %>
    </div>
  <% end %>
</div>
  1. 新增JS代码监听第一个下拉框的选中事件,动态请求站点数据:
document.addEventListener('DOMContentLoaded', () => {
  const studySelect = document.getElementById('study-select');
  const siteSelect = document.getElementById('site-select');

  studySelect.addEventListener('change', async () => {
    const studyId = studySelect.value;
    // 清空原有选项
    siteSelect.innerHTML = '<option value="">--Choose--</option>';
    
    if (!studyId) return;

    // 请求对应研究的站点列表
    const response = await fetch(`/studies/${studyId}/sites`);
    const sites = await response.json();

    // 填充第二个下拉框
    sites.forEach(site => {
      const option = document.createElement('option');
      option.value = site.id;
      option.textContent = site.name;
      siteSelect.appendChild(option);
    });
  });
});
  1. 新增接口路由和控制器方法,返回站点JSON数据
    在config/routes.rb中添加路由:
resources :studies do
  get 'sites', on: :member
end

在StudiesController中添加对应方法:

def sites
  @sites = Site.where(study_id: params[:id])
  render json: @sites.select(:id, :name)
end

方案2:前端预加载过滤(数据量较小时推荐)

无需额外开发接口,提前把全量站点数据注入前端,选中研究后直接本地过滤,实现更简单。

<div class="study-search">
  <%= form_with(url: show_subjects_path(:site_id), method: :get, html: {style: 'font-size:16px'}) do |f| %>
    <%= f.label 'Select Study' %>
    <%= f.select :id, Study.all.collect {|study| [study.title, study.id] },  {:include_blank => "--Choose--"}, id: "study-select" %>
    <%= f.label 'Select Site' %>
    <%= f.select :site_id, [],  {:include_blank => "--Choose--"}, id: "site-select" %>
    <div class="btn btn-sm" style="display: inline">
      <%= f.submit "GO" %>
    </div>
  <% end %>
</div>

<script>
  // 提前按研究ID分组存储所有站点数据
  const siteMap = <%= Site.all.group_by(&:study_id).transform_values {|sites| sites.map {|s| {id: s.id, name: s.name} }}.to_json.html_safe %>;
  
  document.addEventListener('DOMContentLoaded', () => {
    const studySelect = document.getElementById('study-select');
    const siteSelect = document.getElementById('site-select');

    studySelect.addEventListener('change', () => {
      const studyId = studySelect.value;
      siteSelect.innerHTML = '<option value="">--Choose--</option>';
      
      if (!studyId || !siteMap[studyId]) return;
      
      // 直接用预加载数据填充下拉框
      siteMap[studyId].forEach(site => {
        const option = document.createElement('option');
        option.value = site.id;
        option.textContent = site.name;
        siteSelect.appendChild(option);
      });
    });
  });
</script>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.19 09:54:02