Nested Forms with Cocoon

Episode #71 by Teacher's Avatar David Kimura

Summary

Using the unobtrusive gem, Cocoon, learn how to handle multiple models in a single form with accepts_nested_attributes_for.
rails form javascript 4:40

Resources

Summary

# _form.html.erb
<%= simple_form_for(@todo_list) do |f| %>
  <%= f.error_notification %>

  <div class="form-inputs">
    <%= f.input :name %>
  </div>
  <h3>Tasks</h3>

  <table class='table'>
    <thead>
      <tr>
        <th></th>
        <th>Task Name</th>
        <th>Completed</th>
      </tr>
    </thead>
    <tbody class='tasks'>
      <%= f.simple_fields_for :tasks do |builder| %>
        <%= render 'task_fields', f: builder %>
      <% end %>
    </tbody>

  </table>

  <div class="form-actions">
    <%= f.button :submit %>
    <%= link_to_add_association 'Add Task', f, :tasks, class: 'btn btn-primary', data: { association_insertion_node: '.tasks', association_insertion_method: :append } %>
  </div>
<% end %>

# _task_fields.html.erb
<tr class="nested-fields">
  <td>
    <%= link_to_remove_association "remove task", f, class: 'btn btn-primary btn-xs' %>
  </td>
  <td><%= f.input :description, label: false %></td>
  <td><%= f.input :done, label: false %></td>
</tr>

# Gemfile
gem 'cocoon'

# application.js
//= require cocoon

# task.rb
class Task < ApplicationRecord
  belongs_to :todo_list, optional: true
end

# todo_list.rb
class TodoList < ApplicationRecord
  has_many :tasks, dependent: :destroy
  accepts_nested_attributes_for :tasks, 
                                allow_destroy: true, 
                                reject_if: proc { |att| att['description'].blank? }
end

# todo_lists_controller.rb
    def todo_list_params
      params
        .require(:todo_list)
        .permit(:name, tasks_attributes: Task.attribute_names.map(&:to_sym).push(:_destroy))
    end