Implement Rails nested attributes with dynamic add/remove functionality using Turbo Streams and Simple Form. Use when building forms where users need to manage multiple child records (has_many associations), add/remove nested items without page refresh, or create bulk records inline.
68
82%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Implement Rails nested attributes with dynamic add/remove functionality using Turbo Streams and Simple Form. This pattern allows users to add and remove associated records inline within a parent form.
accepts_nested_attributes_for :association_name in the model or form objectCreate a form that includes:
simple_fields_for for rendering existing nested items#accessories)Example:
= 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.sizeAdd Button Partial (_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 }Create a partial (e.g., _item_fields.html.slim) that:
Example:
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' }Implement a new action that:
index parameter for tracking positionExample:
def new
@item = Item.new
endCreate a new.turbo_stream.slim view that:
index parameter to ensure unique field namesExample:
= 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 }Create a Stimulus controller to handle client-side removal:
Example 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 to model/form objectsimple_fields_for and container elementnew action with index supportPass filtered collections to nested partials:
= render 'item_fields', f: ff, resource:, collection: resource.itemsIf there is not an existing new route in use, use the following pattern
resources :items, only: [:new]If one does exist, create a new namespaced controller
namespace :parent do
resources :items, only: [:new]
enddef item_params
params.require(:parent).permit(
:category,
:subcategory,
items_attributes: %i[
name
quantity
part_id
optional
hidden
]
)
end52c069a
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.