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.