Nested forms are forms that handle nested models and attributes in one form; e.g. a project with its tasks or an invoice with its line items.
Before Rails 6, Cocoon is a good choice for creating dynamic nested forms. But Cocoon needs jQuery to work well, it’s a very old library on modern-day frontend frameworks.
When Stimulus is came out, Rails devs is suggested to use Stimulus as Javascript library in their projects. So, I created a gem for handling dynamic nested forms with Stimulus JS.
Same as Cocoon, but using StimulusJS
Handle dynamic nested forms, same as Cocoon, but using StimulusJS
Installation
Install the gem and add to the application’s Gemfile by executing:
Or inside the Gemfile add the following
$ gem 'rondo_form', '~> 0.2.1'
Run the installation task:
$ rails g rondo_form:install
Usage
For example, we have Project
model, which has has_many
relationship with Task
model:
rails g scaffold Project name:string description:string
rails g model Task description:string done:boolean project:belongs_to
Sample with SimpleForm
In your projects/_form
partial:
<%= simple_form_for(@project) do |f| %>
<div class="form-inputs">
<%= f.input :name %>
<%= f.input :description %>
</div>
<h3 class="text-xl mt-4">Tasks</h3>
<div class="my-2" data-controller="nested-rondo">
<%= f.simple_fields_for :tasks do |task| %>
<%= render "task_fields", f: task %>
<%
…
Rondo Form is easy to use, it has the same tag helpers name as cocoon: link_to_add_association
, link_to_remove_association
.
This gem does not need JS dependencies to work with, when you run under command, it will generate nested_rondo_controller.js
in your app/javascript/controllers/
folder:
rails g rondo_form:install
And auto import this controller into index.js.
import NestedRondoController from "./nested_rondo_controller"
application.register("nested-rondo", NestedRondoController)
You must add data-controller="nested-rondo"
to an element, that wraps fields_for
and link_to_add_association
helper.
For example, we have Project model, which has has_many
relationship with Task model.
In your projects/_form
partial:
<%= simple_form_for(@project) do |f| %>
<div class="form-inputs">
<%= f.input :name %>
<%= f.input :description %>
</div>
<h3>Tasks</h3>
<div data-controller="nested-rondo">
<%= f.simple_fields_for :tasks do |task| %>
<%= render "task_fields", f: task %>
<% end %>
<div class="links">
<%= link_to_add_association "Add Task", f, :tasks %>
</div>
</div>
<%= f.button :submit %>
<% end %>
In your _task_fields
partial:
<div class="nested-fields">
<%= f.input :description %>
<%= f.input :done, as: :boolean %>
<%= link_to_remove_association "Remove Task", f %>
</div>
This sample I built with SimpleForm gem, but the idea is the same with Rails standard form.