Update multiple checkboxes on association model through nested attributes with Cocoon gem in Rails

拟墨画扇 提交于 2021-02-10 06:43:47

问题


I found this answer here which should be able to solve my problem but turns out that answer is for a one to many association. Btw, i'm using Rails 5.2

In my many to many, I have a Task model which has_many test_methods through tasks_test_methods where tasks_test_methods is a join table.

class Task < ApplicationRecord
  has_many :tasks_test_methods, inverse_of: :task, :dependent => :destroy 
  has_many :test_methods, :through => :tasks_test_methods

  accepts_nested_attributes_for :tasks_test_methods, reject_if: :all_blank, allow_destroy: true
end

Here is my join table model tasks_test_methods

class TasksTestMethod < ApplicationRecord 
  belongs_to :task
  belongs_to :test_method

  accepts_nested_attributes_for :test_method, :reject_if => :all_blank
end

And my TestMethod model

class TestMethod < ApplicationRecord
  has_many :tasks_test_methods, inverse_of: :test_method, :dependent => :destroy 
  has_many :tasks, :through => :tasks_test_methods
end

My TasksTestMethods table is simple and only receives the task_id and test_method_id

create_table "tasks_test_methods", id: false, force: :cascade do |t|
    t.bigint "task_id", null: false
    t.bigint "test_method_id", null: false
end

I have a list of predefined test_methods that are displayed and needs to be added to a task upon creation. For example.

<TestMethod id: 1, name: "Visual Testing (VT)", code: nil, description: nil, category_id: 1, created_at: "2018-05-15 12:11:38", updated_at: "2018-05-15 12:11:38">

I want to have all these test_methods in the new task form like this

So when users checks a checkbox with label Visual Testing a new record is created like this (an example from the rails console):

2.5.1 :176 > params = { task: { name: "Test", client_id: 1, tasks_test_methods_attributes: [test_method_id: 1]}}
 => {:task=>{:name=>"Test", :client_id=>1, :tasks_test_methods_attributes=>[{:test_method_id=>1}]}}
2.5.1 :177 > task = Task.create!(params[:task])
<ActiveRecord::Associations::CollectionProxy [#<TasksTestMethod task_id: 16, test_method_id: 2>]>

This is my new task form, here I'm using cocoon gem already in the fields_for part

= form_with(model: @task) do |f|
  .form-group
     %label Client Name:
        = f.select(:client_id, @clients.collect { |client| [ client.name, client.id ] }, { include_blank: "Select Client" }, { :class => 'form-control select2', style: 'width: 100%;' })
   .col-md-12
      = f.fields_for :tasks_test_method do |task_test_method|
        = render 'test_method_fields', f: task_test_method

And in the test_method_fields partial I have this:

= collection_check_boxes :tasks_test_method, :test_method_ids, @test_methods, :id, :name do |box| 
  .form-group.row 
    .form-check.col-md-3
      = box.check_box
      %span.ml-2 
        = box.label

The above code shows the checkboxes as expected, but doesn't work as expected. Can someone guide me on how to make this work?

I have also whitelisted these in the TasksController

tasks_test_methods_attributes: [:id, test_method_ids: []])

This is the code form produces for each checkbox in the browser console

<div class="form-check col-md-3">
  <input type="checkbox" value="1" name="tasks_test_method[test_method_ids][]" id="tasks_test_method_test_method_ids_1">
  <span class="ml-2">
   <label for="tasks_test_method_test_method_ids_1">Visual Testing (VT)</label>
  </span>
</div>

The problem is I can't get the tasks_test_methods_attributes into the params, also when I click on 2 of the test_methods and try to add a task, for example, I get this in the console:

Parameters: {"utf8"=>"✓", "authenticity_token"=>"cuEnQNeh4iT+38AwsNduQGce5kxFcS7sFw0SAgpdvxJjVziFTnrSgzdq6LODNDxzhan2ne31YMeCcJdYL8VoSQ==", "task"=>{"client_id"=>"", "name"=>"", "department_id"=>"", "start_date"=>"", "offshore_flag"=>"0", "order_number"=>"", "description"=>"", "task_manager_id"=>"", "language_id"=>"", "client_ref"=>"", "testsite"=>"", "contact_person_phone"=>"", "contact_person"=>"", "contact_person_email"=>"", "information_to_operator"=>""}, "tasks_test_method"=>{"test_method_ids"=>["", "1", "2"]}, "commit"=>"Create Task"} 

When I try to create a Task, it's been created but without the tasks_test_methods_attributes part in the params.

This is my TasksController new action

def new
  @task = Task.new 
  TestMethod.all.each do |test_method|
    @task.tasks_test_methods.build(test_method_id: test_method.id)
  end
 end

回答1:


Finally got an answer for this task. My whole approach was wrong, trying to insert directly into a join table (That's never going to work!).

This article helped me figure out my mistake, after reading it multiple times. Ended up fixing the problem in 3 mins, after taking about 3 days to search for a solution.

In your model, accept nested attributes for the join table like so:

accepts_nested_attributes_for :tasks_test_methods, reject_if: :all_blank, allow_destroy: true

While reading the cocoon gem documentation, I found this statement that makes sense. When saving nested items, theoretically the parent is not yet saved on validation, so rails needs help to know the link between relations. There are two ways: either declare the belongs_to as optional: false, but the cleanest way is to specify the inverse_of: on the has_many. That is why we write : has_many :tasks, inverse_of: :test_method

Now in my Task model, I have

has_many :tasks_test_methods, inverse_of: :task, :dependent => :destroy 
has_many :test_methods, :through => :tasks_test_methods

Also in my TestMethod model, I have

has_many :tasks_test_methods, inverse_of: :test_method, :dependent => :destroy 
has_many :tasks, :through => :tasks_test_methods

And then in the TasksController I added this to the params, test_method_ids: []

And finally in the form I have this:

.form-group
   = f.collection_check_boxes :test_method_ids, @test_methods, :id, :name do |test_method|
      .form-check.mb-4
         = test_method.check_box(class: "form-check-input")
         %label.form-check-label 
            %span.ml-2  
              = test_method.label

And when now your HTML element should look like this:

<div class="form-check mb-4">
  <input class="form-check-input" type="checkbox" value="1" name="task[test_method_ids][]" id="task_test_method_ids_1">
  <label class="form-check-label">
   <span class="ml-2">
     <label for="task_test_method_ids_1">Visual Testing (VT)</label>
   </span>
  </label>
</div>

Hope this helps.



来源:https://stackoverflow.com/questions/50397979/update-multiple-checkboxes-on-association-model-through-nested-attributes-with-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!