Dynamic Nested Forms in Ruby on Rails

With dynamic nested forms, you can create forms that automatically calculate fields based on the information that has been entered into previous fields. If you’re building a form for your online store, for example, you might want to create a product listing form with several different fields: name, category and price. If the user selects “blue jeans” as their category and then enters $50 as their price, then the form will automatically calculate all of the necessary information—including tax—and display it in an easy-to-read format.

Dynamic nested forms are also an excellent way to reduce the amount of code needed to create your form. It’s a lot easier than building out each section manually, and it’s also more secure and more scalable than using a single form.

In this post, we will discuss how to create dynamic nested forms in Ruby on Rails using Cocoon gem. We will use two models, product and FAQ. The product can have multiple FAQs, which we will add manually while creating it.

1
gem "cocoon"
1
yarn add @nathanvda/cocoon

In app/javascripts/packs/application.js:

1
2
3
// app/javascripts/packs/application.js

require("@nathanvda/cocoon")

Let’s do some database stuff.

1
2
3
rails g scaffold Product name price:integer
rails g model Faq question answer product:belongs_to
rails db:migrate

In app/models/product.rb:

1
2
3
4
5
6
# app/models/product.rb

class Product < ApplicationRecord
 has_many :faqs, inverse_of: :product
 accepts_nested_attributes_for :faqs, reject_if: :all_blank, allow_destroy: true
end

In app/controllers/products_controller.rb:

1
2
3
4
5
6
# app/controllers/products_controller.rb

def product_params
 params.require(:product).permit(:name, :price, faqs_attributes: [:id, :question, :answer, :_destroy])
end

In app/views/products/_form.html.erb:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# app/views/products/_form.html.erb

<%= semantic_form_for @product do |f| %>
 <% f.inputs do %>
   <%= f.input :name %>
   <%= f.input :price %>
   <h3>FAQs</h3>
   <div id='faqs'>
     <%= f.semantic_fields_for :faqs do |faq| %>
       <%= render 'faq_fields', :f => faq %>
     <% end %>
     <div class='links'>
       <%= link_to_add_association 'add faq', f, :faqs %>
     </div>
   </div>
   <% f.buttons do %>
     <%= f.submit 'Save' %>
   <% end %>
 <% end %>
<% end %>

Create a new partial app/views/products/_faq_fields.html.erb:

1
2
3
4
5
6
7
8
9
# app/views/products/_faq_fields.html.erb

<div class='nested-fields'>
 <%= f.inputs do %>
   <%= f.input :question %>
   <%= f.input :answer %>
   <%= link_to_remove_association "remove faq", f %>
 <% end %>
</div>

You’ll also need to make certain that jQuery is properly added to your project.