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

在Ruby on Rails的HAML中创建复选框下拉菜单的方法

解决方案

要把杂乱的复选框改成带复选框的下拉菜单,我们可以基于Bootstrap自定义下拉组件,配合Simple Form的collection_check_boxes实现整洁排版,同时添加交互优化(比如显示选中项、全选/取消全选)。

修改后的完整代码(Haml)

.card-header.d-flex.align-items-center
  %span Listing Students

  = link_to 'New Student', new_student_path, class: 'btn btn-outline-secondary ms-auto'
  %button.btn.btn-outline-secondary.dropdown-toggle.mr-2#filter-button{type: "button", "data-bs-toggle": "dropdown", "aria-expanded": "false"}
    Filter
  %ul.dropdown-menu{aria-labelledby: "filter-button", style: "min-width: 300px;"}
    %li.p-3
      = simple_form_for :filter, url: filter_students_path, method: :post do |f|
        = f.input :givenname, wrapper: :vertical_form, input_html: {class: 'form-control'}
        
        # 课程筛选:带复选框的下拉
        %div.mb-3
          %button.btn.btn-outline-secondary.w-100.text-start.dropdown-toggle{type: "button", "data-bs-toggle": "dropdown", "aria-expanded": "false"}
            %span#selected-courses 选择课程
          %ul.dropdown-menu.w-100
            %li.p-2
              = f.collection_check_boxes(:course_ids, Course.all, :id, :course_code, include_hidden: false) do |b|
                %div.form-check.mb-1
                  = b.check_box(class: 'form-check-input')
                  = b.label(class: 'form-check-label')
              %div.d-flex.justify-content-between.mt-2
                %button.btn.btn-sm.btn-outline-secondary.select-all{type: 'button'} 全选
                %button.btn.btn-sm.btn-outline-secondary.deselect-all{type: 'button'} 取消全选
        
        # 地点筛选:同理修改
        %div.mb-3
          %button.btn.btn-outline-secondary.w-100.text-start.dropdown-toggle{type: "button", "data-bs-toggle": "dropdown", "aria-expanded": "false"}
            %span#selected-locations 选择地点
          %ul.dropdown-menu.w-100
            %li.p-2
              = f.collection_check_boxes(:location_ids, Location.all, :id, :location_name, include_hidden: false) do |b|
                %div.form-check.mb-1
                  = b.check_box(class: 'form-check-input')
                  = b.label(class: 'form-check-label')
              %div.d-flex.justify-content-between.mt-2
                %button.btn.btn-sm.btn-outline-secondary.select-all-loc{type: 'button'} 全选
                %button.btn.btn-sm.btn-outline-secondary.deselect-all-loc{type: 'button'} 取消全选
        
        # 行业筛选:同理修改
        %div.mb-3
          %button.btn.btn-outline-secondary.w-100.text-start.dropdown-toggle{type: "button", "data-bs-toggle": "dropdown", "aria-expanded": "false"}
            %span#selected-sectors 选择行业
          %ul.dropdown-menu.w-100
            %li.p-2
              = f.collection_check_boxes(:sector_ids, Sector.all, :id, :sector_name, include_hidden: false) do |b|
                %div.form-check.mb-1
                  = b.check_box(class: 'form-check-input')
                  = b.label(class: 'form-check-label')
              %div.d-flex.justify-content-between.mt-2
                %button.btn.btn-sm.btn-outline-secondary.select-all-sec{type: 'button'} 全选
                %button.btn.btn-sm.btn-outline-secondary.deselect-all-sec{type: 'button'} 取消全选
        
        %div.d-flex.gap-2
          = f.submit 'Search', class: 'btn btn-primary'
          = link_to 'Reset', students_path, class: 'btn btn-outline-secondary'

配套JavaScript(添加到页面或JS文件)

document.addEventListener('DOMContentLoaded', function() {
  // 通用更新选中项文本的函数
  function updateSelectedText(checkboxSelector, spanId, defaultText) {
    const checkboxes = document.querySelectorAll(checkboxSelector);
    const span = document.getElementById(spanId);
    
    const selected = Array.from(checkboxes)
      .filter(cb => cb.checked)
      .map(cb => cb.nextElementSibling.textContent);
    span.textContent = selected.length > 0 ? selected.join(', ') : defaultText;
  }

  // 课程筛选交互
  const courseCheckboxes = 'input[name="filter[course_ids][]"]';
  updateSelectedText(courseCheckboxes, 'selected-courses', '选择课程');
  document.querySelectorAll(courseCheckboxes).forEach(cb => {
    cb.addEventListener('change', () => updateSelectedText(courseCheckboxes, 'selected-courses', '选择课程'));
  });
  document.querySelector('.select-all').addEventListener('click', () => {
    document.querySelectorAll(courseCheckboxes).forEach(cb => cb.checked = true);
    updateSelectedText(courseCheckboxes, 'selected-courses', '选择课程');
  });
  document.querySelector('.deselect-all').addEventListener('click', () => {
    document.querySelectorAll(courseCheckboxes).forEach(cb => cb.checked = false);
    updateSelectedText(courseCheckboxes, 'selected-courses', '选择课程');
  });

  // 地点筛选交互
  const locationCheckboxes = 'input[name="filter[location_ids][]"]';
  updateSelectedText(locationCheckboxes, 'selected-locations', '选择地点');
  document.querySelectorAll(locationCheckboxes).forEach(cb => {
    cb.addEventListener('change', () => updateSelectedText(locationCheckboxes, 'selected-locations', '选择地点'));
  });
  document.querySelector('.select-all-loc').addEventListener('click', () => {
    document.querySelectorAll(locationCheckboxes).forEach(cb => cb.checked = true);
    updateSelectedText(locationCheckboxes, 'selected-locations', '选择地点');
  });
  document.querySelector('.deselect-all-loc').addEventListener('click', () => {
    document.querySelectorAll(locationCheckboxes).forEach(cb => cb.checked = false);
    updateSelectedText(locationCheckboxes, 'selected-locations', '选择地点');
  });

  // 行业筛选交互
  const sectorCheckboxes = 'input[name="filter[sector_ids][]"]';
  updateSelectedText(sectorCheckboxes, 'selected-sectors', '选择行业');
  document.querySelectorAll(sectorCheckboxes).forEach(cb => {
    cb.addEventListener('change', () => updateSelectedText(sectorCheckboxes, 'selected-sectors', '选择行业'));
  });
  document.querySelector('.select-all-sec').addEventListener('click', () => {
    document.querySelectorAll(sectorCheckboxes).forEach(cb => cb.checked = true);
    updateSelectedText(sectorCheckboxes, 'selected-sectors', '选择行业');
  });
  document.querySelector('.deselect-all-sec').addEventListener('click', () => {
    document.querySelectorAll(sectorCheckboxes).forEach(cb => cb.checked = false);
    updateSelectedText(sectorCheckboxes, 'selected-sectors', '选择行业');
  });
});

关键说明

  1. 排版优化:用Bootstrap的form-check类包裹复选框和标签,避免杂乱;下拉菜单设置固定宽度,保证布局规整。
  2. 交互增强:添加选中项文本显示,用户能直观看到已选内容;全选/取消全选按钮提升操作效率。
  3. 表单兼容性:保留collection_check_boxes的参数提交逻辑,后端无需修改即可接收数组格式的筛选参数。
  4. include_hidden: false:移除默认生成的隐藏空值输入框,避免无选中时提交空字符串数组(若需要兼容无选中场景可保留)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 05:24:58