ActiveAdmin表单选择用户后联动显示其关联书籍的查询方法
实现方案
别想着只换个查询语句就搞定实时联动——后端渲染表单的时候拿不到用户前端后来选中的用户ID,静态查询只能解决页面首次加载的选项正确性,必须配合少量JS才能实现选完用户自动过滤书籍的效果,具体步骤如下:
基础关联确认
现有模型关联符合一对多的业务要求,不需要调整:
class User has_many :books end class Book belongs_to :user end
表单代码修改
首先修正原代码笔误:ActiveAdmin的表单DSL是单数形式form不是forms,然后替换原来的Book.all,给两个下拉框加ID方便JS绑定事件,同时预加载用户和书籍的映射关系:
form do |f| f.inputs do f.input :user, as: :select, collection: User.all.pluck(:name, :id), input_html: { id: 'user-select' } # 替换原Book.all:编辑已有记录时加载当前用户关联的书籍,新建记录时默认空集合 f.input :books, as: :select, collection: f.object.persisted? ? f.object.user.books : Book.none, input_html: { id: 'book-select' } end # 书籍数据量不大的场景直接把用户-书籍映射存在隐藏标签里,不用额外写接口 f.inputs do div id: 'user-books-map', 'data-map' => User.includes(:books).all.map{ |u| [u.id, u.books.pluck(:title, :id)] }.to_h.to_json, style: 'display: none;' end f.actions end
替换Book.all的核心逻辑:
- 编辑场景(表单对象已经持久化到数据库):直接取当前记录关联用户的书籍集合
f.object.user.books,保证打开编辑页时书籍选项默认正确 - 新建场景:默认返回空集合
Book.none,等用户选完用户再通过JS加载对应选项
联动JS逻辑
把下面的代码加到ActiveAdmin的全局JS文件(一般路径是app/assets/javascripts/active_admin.js)里,监听用户下拉的变更事件,动态刷新书籍选项:
$(document).on('change', '#user-select', function() { const userId = $(this).val(); const bookSelect = $('#book-select'); bookSelect.empty(); if (!userId) return; // 读取预存的映射,渲染对应书籍选项 const bookMap = JSON.parse($('#user-books-map').data('map')); (bookMap[userId] || []).forEach(book => { bookSelect.append(new Option(book[0], book[1])); }); });
如果书籍总量很大,不适合全量预加载到页面,可以加一个轻量后端接口接收
user_id参数返回对应用户的书籍列表,JS选中用户后请求接口拉取数据即可,核心逻辑和上面一致。
内容的提问来源于stack exchange,提问作者Akshit Thakur Ak
相关产品推荐
相关产品推荐

