Getting Started
Components
- Animated Number
- Auto Submit
- Carousel
- Character Counter
- Chartjs
- Checkbox Select All
- Clipboard
- Color Picker
- Confirmation New
- Content Loader
- Dialog
- Dropdown
- Glow
- Lightbox
- Notification
- Password Visibility
- Places Autocomplete
- Popover
- Prefetch
- Rails Nested Form
- Read More
- Remote Rails
- Reveal Controller
- Scroll Progress
- Scroll Reveal
- Scroll To
- Sortable
- Sound
- Textarea Autogrow
- Timeago
Components
Rails Nested Form
A Stimulus controller to create new fields on the fly to populate your Rails relationship with accepts_nested_attributes_for.
Nested attributes allow you to save attributes on associated records through the parent.
Video Tutorial
Dean DeHart has released a presentation video on how to use this package with a real life example with Ruby on Rails.
Installation
Install the package
Terminal$ yarn add @stimulus-components/rails-nested-form
Register the controller in your application
app/javascript/controllers/index.jsimport { Application } from '@hotwired/stimulus' import RailsNestedForm from '@stimulus-components/rails-nested-form' const application = Application.start() application.register('nested-form', RailsNestedForm)
Example
Rails Nested Form
Usage
In your models:
class User < ApplicationRecord
has_many :todos
accepts_nested_attributes_for :todos, reject_if: :all_blank, allow_destroy: true
end
class Todo < ApplicationRecord
belongs_to :user
end
In your controller:
class UsersController < ApplicationController
def update
if user.update(user_params)
redirect_to users_path
else
render :edit
end
end
private
def user_params
params
.require(:user)
.permit(
todos_attributes: [:id, :_destroy, :description]
)
end
end
To DRY up the code, we extract the fields in a partial called todo_form
to use it in the template with a new Todo
and in the default fields_for
.
<%= form_with model: @user, data: { controller: 'nested-form', nested_form_wrapper_selector_value: '.nested-form-wrapper' } do |f| %>
<template data-nested-form-target="template">
<%= f.fields_for :todos, Todo.new, child_index: 'NEW_RECORD' do |todo_fields| %>
<%= render "todo_form", f: todo_fields %>
<% end %>
</template>
<%= f.fields_for :todos do |todo_fields| %>
<%= render "todo_form", f: todo_fields %>
<% end %>
<!-- Inserted elements will be injected before that target. -->
<div data-nested-form-target="target"></div>
<button type="button" data-action="nested-form#add">Add todo</button>
<%= f.submit 'Save todos' %>
<% end %>
<div class="nested-form-wrapper" data-new-record="<%= f.object.new_record? %>">
<%= f.label :description %>
<%= f.text_field :description %>
<button type="button" data-action="nested-form#remove">Remove todo</button>
<%= f.hidden_field :_destroy %>
</div>
As explained in the documentation, we need to specify the child_index
and replace its value in JavaScript because the index needs to be unique for each fields.
Configuration
Attribute | Default | Description | Optional |
---|---|---|---|
data-nested-form-wrapper-selector-value | .nested-form-wrapper | Selector to find the wrapper. | ✅ |
The remove feature is completely optional.
Extending Controller
You can use inheritance to extend the functionality of any Stimulus component:
import NestedForm from "@stimulus-components/rails-nested-form"
export default class extends NestedForm {
connect() {
super.connect()
console.log("Do what you want here.")
}
}
This controller will automatically have access to targets defined in the parent class.
If you override the connect
, disconnect
or any other methods from the parent, you'll want to call super.method()
to make sure the parent functionality is executed.