使用Rails中的Cocoon gem通过嵌套属性更新关联模型上的多个复选框

时间:2018-05-17 18:11:00

标签: ruby-on-rails ruby nested-forms cocoon-gem

我发现这个答案here应该能够解决我的问题但事实证明答案是针对一对多的关联。顺便说一下,我正在使用Rails 5.2

在我的多对多中,我有一个任务模型,其中包含test_methodstasks_test_methods,其中tasks_test_methods是一个连接表。

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

这是我的联接表模型tasks_test_methods

class TasksTestMethod < ApplicationRecord 
  belongs_to :task
  belongs_to :test_method

  accepts_nested_attributes_for :test_method, :reject_if => :all_blank
end

我的TestMethod模型

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

我的TasksTestMethods表很简单,只接收task_idtest_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

我有一个显示的预定义test_methods列表,需要在创建时添加到task。例如。

<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">

我希望新test_methods形式中的所有这些task都是这样的 enter image description here

因此,当用户选中带有标签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 = 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 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

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: []])

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

<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>

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

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"} 

当我尝试创建Task时,它已创建但没有tasks_test_methods_attributes部分的参数。

这是我的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 个答案:

答案 0 :(得分:0)

终于得到了这个任务的答案。我的整个方法都是错误的,试图直接插入连接表(它永远不会起作用!)。

经过多次阅读后,

This article帮助我弄明白了我的错误。在花了大约3天的时间来寻找解决方案之后,在3分钟内解决问题。

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

src <- osmsource_api(url = "https://api.openstreetmap.org/api/0.6/")

在阅读cocoon gem documentation时,我发现这个陈述是有道理的。 accepts_nested_attributes_for :tasks_test_methods, reject_if: :all_blank, allow_destroy: true

现在我的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模型中有

Task

同样在我的has_many :tasks_test_methods, inverse_of: :task, :dependent => :destroy has_many :test_methods, :through => :tasks_test_methods 模型中,我有

TestMethod

然后在has_many :tasks_test_methods, inverse_of: :test_method, :dependent => :destroy has_many :tasks, :through => :tasks_test_methods 我将此添加到参数TasksController

最后以我的形式:

test_method_ids: []

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

.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

希望这会有所帮助。