Foosaurus
Foosaurus

Reputation: 49

rails multi-level nested forms with cocoon and tables

I've successfully implemented one level of nested form with Cocoon and tables. However, I'm having a difficult time wrapping my mind on how to do another nested level. My issue is how to do this with a table. And maybe a table isn't the write way to go at all. Thank you for helping a newbie.

Here are my models:

class Profession < ApplicationRecord
  has_many :procedure_categories, dependent: :destroy
  accepts_nested_attributes_for :procedure_categories, allow_destroy: true
end

And:

class ProcedureCategory < ApplicationRecord
  belongs_to :profession
  has_many :procedures

  accepts_nested_attributes_for :procedures, allow_destroy: true
end

And:

class Procedure < ApplicationRecord
  belongs_to :procedure_category
end

Here is my top level form code:

<%= form_for(@profession) do |f| %>
  <%= render 'shared/profession_error_messages' %>

  <%= f.label :profession %>
  <%= f.text_field :profession, class: 'form-control'  %>

  <%= f.label :description %>
  <%= f.text_field :description, class: 'form-control'  %>

  <%= f.label :active, class: "checkbox inline" do %>
    <%= f.check_box :active %>
    <span>Active profession?</span>
  <% end %>

  <table class='table'>
    <thead>
      <tr>
        <th>Category</th>
        <th>Description</th>
        <th>Display Order</th>
        <th>Selection Type</th>
        <th>Delete</th>
        <th>Edit</th>
      </tr>
    </thead>
    <tbody class="categories">
      <%= f.fields_for :procedure_categories do |procedure_category| %>
        <%= render 'procedure_category_fields', f: procedure_category %>
      <% end %>
    </tbody>
  </table>


  <%= link_to_add_association 'Add Category', f, :procedure_categories, 
    data: { association_insertion_node: '.categories', association_insertion_method: :append } %>

  <br><br>    
  <%= f.submit "Save", class: "btn btn-primary" %>
  <% end %>

And the next partial one level down:

<tr class="nested-fields">
  <td><%= f.text_field :category, class: 'form-control' %></td>
  <td><%= f.text_field :description, class: 'form-control' %></td>
  <td><%= f.text_field :display_order, class: 'form-control' %></td>
    <% cs = options_for_select(controls, f.object.selection_type) %>
  <td><%= f.select :selection_type, cs, class: 'form-control' %></td>
  <td><%= link_to_remove_association "Remove Category", f %></td>
  <% if f.object != nil %>
    <td><%= link_to "Category", edit_procedure_category_path(@profession,f.object) %><td></td>
  <% end %>
</tr>

So, I'm struggling with how to implement the final level of nesting (procedures).

Thank you for listening.

Upvotes: 1

Views: 1316

Answers (1)

fabOnReact
fabOnReact

Reputation: 5942

Use has_many :through

Here are my models:

class Profession < ApplicationRecord
  has_many :procedures, through: categories
  has_many :categories, dependent: :destroy
  accepts_nested_attributes_for :categories, allow_destroy: true
end

Rename this procedure_category model in category

class Category < ApplicationRecord
  belongs_to :profession
  has_many :procedures

  accepts_nested_attributes_for :procedures, allow_destroy: true
end

And:

class Procedure < ApplicationRecord
  belongs_to :category
end

If I miss something you can check the instruction from the rails guide

The controller professions#new action should create the following variables, so that they are available in the view:

def new
    @profession = Profession.new
    @categories = @profession.categories.build
    @procedures = @categories.procedures.build
end

The view uses that variable so store the user inputs and make a post request at /profession/ with those inputs stored in the parameters hash

<%= form_for(@profession) do |f| %>
      <%= f.fields_for :categories do |category| %>
               <%= category.fields_for :procedures do |precedure| %>
               <% end %>
      <% end %>
<% end %>

The fields_for yields a form builder. The parameters' name will be what accepts_nested_attributes_for expects. For example, when creating a user with 2 addresses, the submitted parameters would look like:

This is how your parameters should look like:

{
  'profession' => {
    'name' => 'John Doe',
    'categories_attributes' => {
      '0' => {
        'kind' => 'Home',
        'street' => '221b Baker Street',
        'procedures_attributes' => {
         '0' => {},
         '1' => {}
         }
      },
      '1' => {
        'kind' => 'Office',
        'street' => '31 Spooner Street'
      }
    }
  }
}

so make sure your form is pointing at post url /professions/ and that the routing will trigger the professions#create action

def create
    binding.pry
end

for any problems set a binding pry and check in your console how your parameters are showing up.

Read more at http://guides.rubyonrails.org/form_helpers.html#building-complex-forms

Upvotes: 1

Related Questions