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

时间:2012-01-19 16:02:39

标签: html ruby-on-rails forms serialization multi-select

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

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

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

MySQL数据库表'模型':

  • 包含一个名为subset_array的列,它是一个TEXT字段

班级模型包括以下设置:

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

9 个答案:

答案 0 :(得分:66)

在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

这将删除模型级别的所有空白值。

答案 1 :(得分: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.

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

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

答案 2 :(得分: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

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

答案 3 :(得分:8)

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

For i = 0 To RecordSet.Fields.Count-1
  Worksheets("Sheet1").Cells(i+1, 1).Value = RecordSet.Fields(i).Name
Next i

答案 4 :(得分: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

答案 5 :(得分:3)

在控制器中:

arr = arr.delete_if { |x| x.empty? }

答案 6 :(得分:0)

我通过在页面的Javascript部分写这个来实现它的工作:

$("#model_subset_array").val( <%= @model.subset_array %> );

我看起来更像是:

$("#modela_modelb_ids").val( <%= @modela.modelb_ids %> );

不确定这是否会让我头疼,但现在它运作正常。

答案 7 :(得分:0)

我在更新前使用控制器中的params[:review][:staff_ids].delete("")修复了它。

在我看来:

= form_for @review do |f|
  = f.collection_select :staff_ids, @business.staff, :id, :full_name, {}, {multiple:true}
= f.submit 'Submit Review'

在我的控制器中:

class ReviewsController < ApplicationController
  def create
  ....
    params[:review][:staff_ids].delete("")
    @review.update_attribute(:staff_ids, params[:review][:staff_ids].join(","))
  ....
  end
end

答案 8 :(得分:-3)

使用jQuery:

$('select option:empty').remove(); 

从下拉菜单中删除空白选项的选项。