关于 dynamic-nested-attributes
Dynamic Nested Attributes 是一项 Rails 技能,用于在表单中内联添加和移除关联记录。它结合嵌套属性、Simple Form、Turbo Streams 与 Stimulus,面向需要编辑子记录的开发者。
主要功能
- 01为关联配置嵌套属性
- 02渲染现有子记录字段
- 03用 Turbo Streams 添加字段
- 04用 Stimulus 移除字段
- 05提供路由与参数清单
适用场景
- 01在一个表单中编辑多条关联记录
- 02无需刷新页面添加或移除子项
- 03构建批量创建或编辑表单
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ rolemodel/ rolemodel-skills --skill dynamic-nested-attributes安装后在 Claude Code 中输入/dynamic-nested-attributes确认已加载。
详细介绍
动态嵌套属性
概述
使用 Turbo Streams 和 Simple Form 实现 Rails 嵌套属性的动态添加和移除功能。此模式允许用户在父表单中内联添加和移除关联记录。
使用场景
- 构建需要用户管理多个子记录(has_many 关联)的表单
- 无需刷新页面即可添加或移除嵌套项
- 批量创建或编辑关联记录
- 需要渐进式展示其他字段的表单
关键组件
1. 表单对象或模型
- 接受该关联的嵌套属性
- 在模型或表单对象中使用
accepts_nested_attributes_for :association_name
2. 主表单视图
创建一个包含以下内容的表单:
- 使用
simple_fields_for渲染现有嵌套项 - 一个带有 ID 的容器元素,用于追加新项(例如
#accessories) - 一个用于添加新项并触发 Turbo Stream 请求的链接
示例:
= simple_form_for resource do |f|
= f.simple_fields_for :items do |ff|
= render 'item_fields', f: ff, resource:
= render 'add_button', index: resource.items.size
添加按钮局部视图(_add_button.html.slim):
-# locals: (index:)
= link_to icon('add'), new_parent_item_path(index: index),
id: 'add_button', class: 'btn', data: { turbo_stream: true }
3. 嵌套字段局部视图
创建一个局部视图(例如 _item_fields.html.slim),用于:
- 将字段包裹在一个基于索引的唯一 ID 容器中
- 包含用于移除功能的数据控制器
- 为所有记录显示删除按钮
- 包含嵌套项的所有表单输入
示例:
fieldset id="item_#{f.index}" controller='destroy-nested-attributes'
= f.hidden_field :_destroy, data: { destroy_nested_attributes_target: 'input' }
= f.input :name
= f.input :quantity
.form-row__actions
= button_tag icon('delete'), type: 'button', class: 'btn btn-delete',
data: { action: 'destroy-nested-attributes#perform' }
4. 控制器操作
实现一个 new 操作,用于:
- 构建一个新的嵌套项
- 接受
index参数以跟踪位置
示例:
def new
@item = Item.new
end
5. Turbo Stream 响应
创建一个 new.turbo_stream.slim 视图,用于:
- 使用递增后的索引更新“添加”按钮
- 将新的嵌套字段追加到容器中
- 使用
index参数确保字段名称唯一 - 对于未持久化的父对象,使用符号和空 URL
示例:
= turbo_stream.replace 'add_button', partial: 'add_button', locals: { index: params[:index].to_i + 1 }
= simple_form_for :parent, url: '' do |f|
= f.simple_fields_for :items_attributes, @item, index: params[:index] do |ff|
= turbo_stream.append 'items', partial: 'item_fields', locals: { f: ff }
6. 移除 Stimulus 控制器
创建一个 Stimulus 控制器来处理客户端移除操作:
JavaScript 示例:
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ['input']
static classes = ['destroyed']
connect() {
if (!this.hasDestroyedClass) {
this.element.setAttribute(`data-${this.identifier}-destroyed-class`, 'is-hidden')
}
}
perform() {
this.inputTarget.value = '1'
this.element.classList.add(this.destroyedClass)
}
}
实现检查清单
- 将
accepts_nested_attributes_for添加到模型或表单对象 - 使用
simple_fields_for和容器元素创建主表单 - 创建带移除功能的嵌套字段局部视图
- 实现支持索引的控制器
new操作 - 创建 turbo_stream 响应视图
- 添加用于客户端移除操作的 Stimulus 控制器
- 更新路由以支持嵌套资源创建
- 更新强参数以允许嵌套属性
- 如果使用 Pundit,则添加策略授权
常见模式
根据父项选择动态生成集合
将筛选后的集合传递给嵌套局部视图:
= render 'item_fields', f: ff, resource:, collection: resource.items
路由示例
如果当前没有可用的新建路由,请使用以下模式
resources :items, only: [:new]
如果已有新建路由,请创建一个新的命名空间控制器
namespace :parent do
resources :items, only: [:new]
end
强参数示例
def item_params
params.require(:parent).permit(
:category,
:subcategory,
items_attributes: %i[
name
quantity
part_id
optional
hidden
]
)
end
相关模式
- Turbo Frame 内联编辑
- Stimulus data 控制器集成
- 用于批量操作的表单对象模式
- 关联的策略作用域集合
评论
支持 Markdown