为什么我的Rails多选中第一个元素总是空白,使用嵌入式数组?

rob*_*rty 81 html forms serialization ruby-on-rails multi-select

我正在使用Rails 3.2.0.rc2.我有一个Model,我有一个静态Array,我通过一个表格提供,用户可以选择一个子集Array并将他们的选择保存到数据库,存储在一个列中Model.我在数据库列上使用了serialize,它存储了ArrayRails并正确地将用户的选择转换为Yaml(并在读取该列时返回到数组).我正在使用多选表单输入来进行选择.

我的问题是,我现在拥有它的方式,一切都按照我的预期工作,除了用户的子集数组在发送到服务器时总是有一个空白的第一个元素.

这不是什么大不了的事,我可以编写代码来解决这个事实,但我觉得我只是在做一些语法错误,因为在我看来默认的Rails行为不是故意的添加此空白元素没有任何理由.我一定错过了什么或忘了禁用某种设置.请帮助我理解我所缺少的东西(或者指出一些描述这个的好文档,比我在intertubes上找到的更深入).

MySQL数据库表'模型':

  • 包括一个名为" subset_arrayTEXT"字段的列

类模型包括以下设置:

  • serialize :subset_array
  • ALL_POSSIBLE_VALUES = [value1, value2, value3, ...]

编辑表单模型包括以下输入选项:

  • f.select :subset_array, Model::ALL_POSSIBLE_VALUES, {}, :multiple => true, :selected => @model.subset_array

从客户端PUT到服务器看起来像这样:

  • 假设只选择了value1和value3
  • "model" => { "subset_array" => ["", value1, value3] }

数据库更新如下所示:

  • UPDATE 'models' SET 'subset_array' = '--- \n- \"\"\n- value1\n- value3\n'

正如您所看到的,数组中的这个额外的空白元素是在数据库中发送和设置的.我怎么摆脱它?我的f.select通话中是否缺少参数?

非常感谢赞赏:)

编辑:这是f.select语句生成的HTML代码.看起来好像有一个隐藏的输入被生成,这可能是我的问题的原因?那为什么?

<input name="model[subset_array][]" type="hidden" value>
<select id="model_subset_array" multiple="multiple" name="model[subset_array][]" selected="selected">
    <option value="value1" selected="selected">Value1</option>
    <option value="value2">Value2</option>
    <option value="value3" selected="selected">Value3</option>
    <option...>...</option>
</select>
Run Code Online (Sandbox Code Playgroud)

Bog*_*iev 67

在Rails 4中:

您将能够通过:include_hidden选项.https://github.com/rails/rails/pull/5414/files

作为现在的快速解决方案:您现在可以在模型中使用:

before_validation do |model|
  model.subset_array.reject!(&:blank?) if model.subset_array
end
Run Code Online (Sandbox Code Playgroud)

这只会删除模型级别的所有空白值.

  • @Donato你必须将include_hidden设置为false(include_hidden:false) (2认同)

Mik*_* A. 51

隐藏的领域是导致问题的原因.但它有充分的理由:当取消选择所有值时,您仍然会收到subset_array参数.从Rails文档(您可能需要向右滚动才能看到所有这些):

  # The HTML specification says when +multiple+ parameter passed to select and all options got deselected
  # web browsers do not send any value to server. Unfortunately this introduces a gotcha:
  # if an +User+ model has many +roles+ and have +role_ids+ accessor, and in the form that edits roles of the user
  # the user deselects all roles from +role_ids+ multiple select box, no +role_ids+ parameter is sent. So,
  # any mass-assignment idiom like
  #
  #   @user.update_attributes(params[:user])
  #
  # wouldn't update roles.
  #
  # To prevent this the helper generates an auxiliary hidden field before
  # every multiple select. The hidden field has the same name as multiple select and blank value.
  #
  # This way, the client either sends only the hidden field (representing
  # the deselected multiple select box), or both fields. Since the HTML specification
  # says key/value pairs have to be sent in the same order they appear in the
  # form, and parameters extraction gets the last occurrence of any repeated
  # key in the query string, that works for ordinary forms.
Run Code Online (Sandbox Code Playgroud)

编辑:最后一段建议你在选择某些东西的情况下不应该看到空的,但我认为这是错误的.将此提交给Rails的人(请参阅https://github.com/rails/rails/commit/faba406fa15251cdc9588364d23c687a14ed6885)尝试使用Rails用于复选框的相同技巧(如此处所述:https://github.com/rails/rails/pull/1552),但我不认为它可以用于多选框,因为在这种情况下发送的参数形成一个数组,因此不会忽略任何值.

所以我的感觉是这是一个错误.


小智 11

另一个快速解决方法是使用此控制器过滤器:

def clean_select_multiple_params hash = params
  hash.each do |k, v|
    case v
    when Array then v.reject!(&:blank?)
    when Hash then clean_select_multiple_params(v)
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

这种方式可以在控制器之间重复使用,而无需触及模型层.


Mar*_*tin 10

在Rails 4+中:select_tag上的include_hidden为false

<%= form.grouped_collection_select :employee_id, Company.all, :employees, :name, :id, :name, { include_hidden: false }, { size: 6, multiple: true } %>
Run Code Online (Sandbox Code Playgroud)


小智 5

http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-check_box

疑难杂症

HTML规范说未选中的复选框或选择不成功,因此Web浏览器不会发送它们.遗憾的是,这引入了一个问题:如果发票模型具有付费标记,并且在编辑付费发票的表单中,用户取消选中其复选框,则不会发送任何付费参数.所以,任何质量分配成语都像

@ invoice.update(params [:invoice])不会更新标志.

为了防止这种情况,帮助程序在复选框之前生成一个辅助隐藏字段.隐藏字段具有相同的名称,其属性模仿未选中的复选框.

这样,客户端只发送隐藏字段(表示未选中复选框)或两个字段.由于HTML规范表明键/值对必须以它们在表单中出现的相同顺序发送,并且参数提取得到查询字符串中任何重复键的最后一次出现,这适用于普通表单.

要删除空白值:

  def myfield=(value)
    value.reject!(&:blank?)
    write_attribute(:myfield, value)
  end
Run Code Online (Sandbox Code Playgroud)