Cocoon Gem in Ruby on Rails 7
Tags: rails, ruby, tutorial, gems
Published: 2023-02-17 21:10:35


## Cocoon Gem in Ruby on Rails 7

In this article, we are going to learn how to create a complex form or nested form, and add a dynamic field for any field in our application using the **cocoon gem** in **Rails 7. **Complex or Nested forms which mean we will handle nested models and attributes in one single form.

So, let’s get begin

![Created By [Author](https://medium.com/@rutikkpatel) ([Rutik Patel](https://medium.com/@rutikkpatel))](https://cdn-images-1.medium.com/max/2560/1*1XcKqtbEztk7treexs6S1w.png)

&nbsp;
**→ Rails ~> 7.0.4** and **Ruby ~> 3.1.0** are utilized for this application.

&nbsp;
## Points to be discussed

 1. What is Cocoon Gem?

 2. Implementation of the cocoon in rails 7

 3. Preview

 4. References

&nbsp;
## 1) What is Cocoon Gem?

As we discussed Essentially, this gem is used for nested forms, multiple models, and many field attributes in a single form. For example, we have an address field, so any person can have multiple addresses; hence, storing that data and rendering that data in the cocoon gem is useful.
>  Cocoon makes it easier to handle nested forms. — Cocoon Github

&nbsp;
## 2) Implementation of the cocoon in rails 7

We are going to add the cocoon gem to our employee CRUD application.

Recently, we developed Employee CRUD in Rails 7 with Bootstrap styling.
Thus, using the cocoon gem, we will add the “Address Field” to that CRUD and make it dynamic.

***Here is a reference to my previously published blog for creating CRUD in Rails 7.***
[**CRUD in Rails 7**](https://rutikkpatel.medium.com/crud-in-rails-7-by-rutik-patel-a2bbc942d069)
{% embed https://rutikkpatel.medium.com/cocoon-gem-in-ruby-on-rails-7-784b00e06bc2 %}

&nbsp;
So let’s begin by adding the cocoon gem to the gemfile. With the **cocoon** gem, we also need to add the **jquery-rails** gem as well, so just add these two gems in your **Gemfile**

    gem 'jquery-rails'
    gem 'cocoon'

Then, Run bundle install for installing these gems.

&nbsp;
Now add jquery.js to **assets.rb **file for precompiling.

    Rails.application.config.assets.precompile += %w( jquery.js )

After doing this just Remember to run rails assets:precompile

&nbsp;
Let's include jquery in the **application.html.erb**

    <%= javascript_include_tag "jquery" %>

→ *When the main page is loaded, this line includes jQuery first in our application.

**This entire setup is for jQuery only.**

&nbsp;
Let’s use importmap (a new feature in Rails 7) to add the cocoon gem:

    bin/importmap pin @nathanvda/cocoon

→ “**@nathanvda/cocoon**” will be pinned in the importmap.rb file.

&nbsp;
Finally, for the cocoon gem, instead of importing **cocoon**, you need to import “@nathanvda/cocoon” in **application.js**.

Here is the syntax for that.

    import "@nathanvda/cocoon"

Now that we’ve finished with Cocoon and jQuery, let’s add the code for dynamic nested fields.

---

As I mentioned about employee CRUD, in our CRUD we have these fields, as you can see in the below snapshot.

![schema.rb of E[mployee CRUD](https://rutikkpatel.medium.com/crud-in-rails-7-by-rutik-patel-a2bbc942d069)](https://cdn-images-1.medium.com/max/2692/1*7owChpUHfkiYbnzhfxrCBg.png)

&nbsp;
As we want to add an address field, we have to generate an address model with house_number, society_name, area, city fields. So for that, fire this command.

    rails g model Address house_number:integer society_name area city

![Created and invoked file after generating model](https://cdn-images-1.medium.com/max/2000/1*5UA12C9vp8SEdLzFfSJujg.png)

This will create and invoke some files inside that we need to work with the model file and one migration file only. And in the migration file, there’s some code for creating an Address table with the given fields.

&nbsp;
### **migration file:**

    class CreateAddresses < ActiveRecord::Migration[7.0]
      def change
        create_table :addresses do |t|
          t.integer :house_number
          t.string :society_name
          t.string :area
          t.string :city
    
          t.timestamps
        end
      end
    end

Now we need to **add a reference** to the employee in this file, so just edit this file and add this line: t.references :employees, and it looks like

    class CreateAddresses < ActiveRecord::Migration[7.0]
      def change
        create_table :addresses do |t|
          t.integer :house_number
          t.string :society_name
          t.string :area
          t.string :city
          t.references :employee
    
          t.timestamps
        end
      end
    end

&nbsp;
If you want to pass null :false with a reference, you can do that also, but here we are keeping it simple.

Now run rails db:migrate it will make some changes to our schema. adding a new addresses table with the given fields and also adding employee_id as a reference in it.

&nbsp;
Apart from it, suppose you don’t want to make any changes in the migration file for giving references and want to add references using another migration, so you need to follow these steps:

**Run this code**

    rails g migration AddEmployeeToAddress employee:references

It will generate one migration with the following code:

    class AddEmployeeToAddress < ActiveRecord::Migration[7.0]
      def change
        add_reference :addresses, :employee, null: false, foreign_key: true
      end
    end

and migrate it using rails db:migrate

&nbsp;
The work of migrating files has been completed; now let’s move on to the model file.

open **app/models/employee.rb** and **paste** the below code.

    class Employee < ApplicationRecord
      has_many :addresses
      accepts_nested_attributes_for :addresses,
                                    allow_destroy: true,
                                    reject_if: proc { |att| att['house_number'].blank? || ['society_name'].blank? || ['city'].blank? || ['area'].blank? }
    end

&nbsp;
open **app/models/address.rb** and **paste** the below code.

    class Address < ApplicationRecord
      belongs_to :employee
    end

That’s it for model. Now it is the turn of the controller file.

&nbsp;
Open **employees_controller.rb** and add parameters for the address_attributes field.

addresses_attributes:[:id, :house_number, :society_name, :area, :city, :_destroy]

**→ Note :*we are also passing the id here to ensure that the fields are not duplicated while editing the data.***

&nbsp;
**Previous params :**

    params.require(:employee).permit(:employee_name, :gender, { hobbies: [] })

&nbsp;
**New params :**

    params.require(:employee).permit(:employee_name, :gender, { hobbies: [] }, addresses_attributes: [:id, :house_number, :society_name, :area, :city, :_destroy])

&nbsp;
After that, you’ll need to add partials and some code for rendering dynamic fields.

Add the following code into the **new.html.erb** file:

    <div class="addresses">
      <%= f.fields_for :addresses do |address| %>
        <%= render 'address_fields', f: address %>
      <% end %>
    </div>
    <%= link_to_add_association "add address", f, :addresses, data: {association_insertion_node: '.addresses', association_insertion_method: :append} %>

&nbsp;
So now we have our entire **new.html.erb** and it contains

    <div class="set-center flex-column">
      <div class="card shadow" style="width: 36rem;">
        <div class="card-header">
          <h2 class="text-center">Employee Form</h2>
        </div>
        <div class="card-body">
          <!--Error Message-->
          <% if flash[:errors] %>
            <% flash[:errors].each do |error| %>
              <p class="text-danger"><%= error %></p>
            <% end %>
          <% end %>
          <!-- Form Started Here -->
          <%= form_with model: @employee do |f| %>
            <%= f.label :employee_name, "Employee Name :", class:"mt-3" %>
            <%= f.text_field :employee_name ,placeholder: "Enter Employee's Name",class:"mb-2 form-control" %>
            <br>
            <!-- Radio Button For Gender -->
            <div class="form-group">
              <%= f.label "Gender :" %>
              <%= f.radio_button :gender, "male" %>
              <%= f.label :gender, "Male" %>
              <%= f.radio_button :gender, "female" %>
              <%= f.label :gender, "Female" %>
            </div>
            <br>
            <!-- Checkbox For Hobbies -->
            <div class="form-group">
              <%= f.label "Hobbies :" %>
              <%= f.check_box :hobbies, { multiple: true },"Reading", false %>
              <%= f.label :hobbies, "Reading" %>
              <%= f.check_box :hobbies, { multiple: true },"Photography", false %>
              <%= f.label :hobbies, "Photography" %>
              <%= f.check_box :hobbies, { multiple: true },"Travelling", false %>
              <%= f.label :hobbies, "Travelling" %>
            </div>
            <br>
            <div class="addresses">
              <%= f.fields_for :addresses do |address| %>
                <%= render 'address_fields', f: address %>
              <% end %>
            </div>
            <%= link_to_add_association "add address", f, :addresses, data: {association_insertion_node: '.addresses', association_insertion_method: :append} %>
            <%= f.submit "Save Employee", class:"btn-primary border-0 rounded-pill shadow p-3" %>
          <% end %>
        </div>
      </div>
    </div>

&nbsp;
Now let’s make a partial file with the name **“_address_fields.html.erb**” in the employee directory.

**app/views/employees/_address_fields.html.erb**

    <!-- Nested Form for f -->
    <div class="nested-fields">
      <%= f.label :house_number, class: "mt-3" %>
      <%= f.number_field :house_number, placeholder: "Enter Your Flat/House Number", class: "mb-3 form-control" %>
      <%= f.label :society_name, class: "mt-3" %>
      <%= f.text_area :society_name, placeholder: "Enter Your Society Name", class: "mb-3 form-control" %>
      <%= f.label :area, class: "mt-3" %>
      <%= f.text_area :area, placeholder: "Enter Your Area Name", class: "mb-3 form-control" %>
      <%= f.label :city, class: "mt-3" %>
      <%= f.text_area :city, placeholder: "Enter Your City Name", class: "mb-3 form-control" %>
      <%= link_to_remove_association "Delete", f %>
    </div>

&nbsp;
The dynamic fields are now displayed in show.html.erb show page as well.

    <!-- Dynamic Address Show Here -->
    <div>
      <% @employee.addresses.each do |address| %>
        House Number: <%= address.house_number %> <br>
        Society Name: <%= address.society_name %> <br>
        Area: <%= address.area %> <br>
        City: <%= address.city %> <br>
      <% end %>
    </div>

**This sort of output will be generated by it.**

![Show Page — Address with dynamic field](https://cdn-images-1.medium.com/max/2000/1*KGUe6ikBMutHr28IkHANCw.png)

That’s it for implementing a dynamic address field using the cocoon gem.

&nbsp;
### Preview :

Click on this link to see the preview
[Showing Add Address and Delete Links](https://cdn-images-1.medium.com/max/3840/1*ub7WlDhCfYu_C3BupKptGg.gif)

---

&nbsp;
Let’s try to make it more appealing by adding some styling now.

Starting with the “add employee” link from new.html.erb or edit.html.erb

### **Previous Code :**

    <%= link_to_add_association "add address", f, :addresses, data: {association_insertion_node: '.addresses', association_insertion_method: :append} %>

&nbsp;
### **Updated Code :**

    <%= link_to_add_association "add address", f, :addresses, data: {association_insertion_node: '.addresses', association_insertion_method: :append} %>

&nbsp;
Additionally, replace the Previous _address_fields with this.

### **_address_fields.html.erb**

     <!-- Nested Form for f -->
    <div class="nested-fields">
      <div class="row">
        <div class="col">
          <%= f.label :house_number, class: "mt-3" %>
          <%= f.number_field :house_number, placeholder: "Enter Your Flat/House Number", class: "mb-3 form-control" %>
        </div>
        <div class="col">
          <%= f.label :society_name, class: "mt-3" %>
          <%= f.text_field :society_name, placeholder: "Enter Your Society Name", class: "mb-3 form-control" %>
        </div>
      </div>
      <div class="row">
        <div class="col">
          <%= f.label :area, class: "mt-3" %>
          <%= f.text_field :area, placeholder: "Enter Your Area Name", class: "mb-3 form-control" %>
        </div>
        <div class="col">
          <%= f.label :city, class: "mt-3" %>
          <%= f.text_field :city, placeholder: "Enter Your City Name", class: "mb-3 form-control" %>
        </div>
      </div>
      <%= link_to_remove_association "Delete", f, class:"btn btn-danger mb-2" %>
    </div>

&nbsp;
### **Output :**

![Add Address Button, Nested Form after styling](https://cdn-images-1.medium.com/max/2000/1*IACpBytviGZ_eQUfxlW8_A.png)

&nbsp;
**show.html.erb**

    <!-- Dynamic Address Show Here -->
    <div>
      <% @employee.addresses.each do |address| %>
        <p>&#10145; House Number: <%= address.house_number %></p>
        <p>&#10145; Society Name: <%= address.society_name %></p>
        <p>&#10145; Area: <%= address.area %></p>
        <p>&#10145; City: <%= address.city %></p>
        <hr>
      <% end %>
    </div>

&nbsp;
### **Output :**

![Show page after styling](https://cdn-images-1.medium.com/max/2136/1*eF1ICoF2Cp-gjVNMQrGkQA.png)

---

&nbsp;
## **3) Final Preview :**

![](https://cdn-images-1.medium.com/max/2000/1*Se_NdkxMuovUtYL8jfZDXQ.gif)

&nbsp;
## 4) References :

**My Github Repo:** [https://github.com/rutikkpatel/Cocoon-Gem-Rails](https://github.com/rutikkpatel/Cocoon-Gem-Rails)

**Cocoon Gem Repo:** [https://github.com/nathanvda/cocoon](https://github.com/nathanvda/cocoon)