mcpField
D
dynamic-nested-attributes

dynamic-nested-attributes

github.com/rolemodel开发737 次安装1 天前更新
代码仓库

关于 dynamic-nested-attributes

Dynamic Nested Attributes 是一项 Rails 技能,用于在表单中内联添加和移除关联记录。它结合嵌套属性、Simple Form、Turbo Streams 与 Stimulus,面向需要编辑子记录的开发者。

主要功能

  1. 01为关联配置嵌套属性
  2. 02渲染现有子记录字段
  3. 03用 Turbo Streams 添加字段
  4. 04用 Stimulus 移除字段
  5. 05提供路由与参数清单

适用场景

  1. 01在一个表单中编辑多条关联记录
  2. 02无需刷新页面添加或移除子项
  3. 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