使用 Rails 中的 Cocoon gem 通过嵌套属性更新关联模型上的多个复选框 [英] Update multiple checkboxes on association model through nested attributes with Cocoon gem in Rails

查看:52
本文介绍了使用 Rails 中的 Cocoon gem 通过嵌套属性更新关联模型上的多个复选框的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在

因此,当用户选中带有标签 Visual Testing 的复选框时,会像这样创建一个新记录(来自 rails 控制台的示例):

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.create!(params[:task])<ActiveRecord::Associations::CollectionProxy [#<TasksTestMethod task_id: 16, test_method_id: 2>]>

这是我的新task表单,这里我在fields_for部分使用cocoon gem

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

test_method_fields 部分我有这个:

= 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

上面的代码按预期显示复选框,但没有按预期工作.有人可以指导我如何完成这项工作吗?

我也在 TasksController

中将这些列入白名单

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

这是为浏览器控制台中的每个复选框生成的代码表单

<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">视觉测试(VT)</label></span>

问题是我无法将 tasks_test_methods_attributes 放入参数中,例如,当我单击 test_methods 中的 2 个并尝试添加任务时,我在控制台中得到这个:

参数:{"utf8"=>"✓", "authenticity_token"=>"cuEnQNeh4iT+38AwsNduQGce5kxFcS7sFw0SAgpdvxJjVziFTnrSgzdq6LODNDxzhan2neJ"QdY=>"cuEnQNeh4iT + 38AwsNduQGce5kxFcS7sFw0SAgpdvxJjVziFTnrSgzdq6LODNDxzhan2neJoS1Y",>"idndx==cJ"QdY";Vhdxzhan2neJoS1Y"名称"=>"", "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"]}, "提交"=>"创建任务"}

当我尝试创建一个 Task 时,它已被创建,但参数中没有 tasks_test_methods_attributes 部分.

这是我的 TasksController 新动作

def new@task = Task.newTestMethod.all.each 做 |test_method|@task.tasks_test_methods.build(test_method_id: test_method.id)结尾结尾

解决方案

终于得到了这个任务的答案.我的整个方法是错误的,试图直接插入连接表(这永远不会奏效!).

这篇文章在阅读后帮助我找出了我的错误它多次.在花了大约 3 天的时间寻找解决方案后,最终在 3 分钟内解决了问题.

在您的模型中,接受连接表的嵌套属性,如下所示:

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

在阅读 cocoon gem 文档时,我发现这个说法很有道理.保存嵌套项时,理论上父项在验证时还没有保存,所以rails需要帮助了解关系之间的联系.有两种方法:要么将belongs_to 声明为可选:false,但最简洁的方法是在has_many 上指定inverse_of:.这就是为什么我们写:has_many :tasks, inverse_of: :test_method

现在在我的 Task 模型中,我有

has_many :tasks_test_methods, inverse_of: :task, :dependent =>:破坏has_many :test_methods, :through =>:tasks_test_methods

也在我的 TestMethod 模型中,我有

has_many :tasks_test_methods, inverse_of: :test_method, :dependent =>:破坏has_many :tasks, :through =>:tasks_test_methods

然后在 TasksController 中,我将其添加到参数中,test_method_ids: []

最后在我有这个形式:

.form-group= f.collection_check_boxes :test_method_ids, @test_methods, :id, :name do |test_method|.form-check.mb-4= test_method.check_box(类:表单检查输入")%label.form-check-label%span.ml-2= test_method.label

现在你的 HTML 元素应该是这样的:

<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">视觉测试(VT)</label></span>

希望这会有所帮助.

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

解决方案

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.

这篇关于使用 Rails 中的 Cocoon gem 通过嵌套属性更新关联模型上的多个复选框的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆