使用 Rails 中的 Cocoon gem 通过嵌套属性更新关联模型上的多个复选框 [英] Update multiple checkboxes on association model through nested attributes with Cocoon gem in Rails
问题描述
我在
因此,当用户选中带有标签 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屋!