Getting Started
Components
- Animated Number
- Auto Submit
- Carousel
- Character Counter
- Chartjs
- Checkbox Select All
- Clipboard
- Color Picker
- Confirmation
- 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.