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

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

我的问题是,我现在拥有它的方式,一切正常,除了用户的子集数组发送到服务器时总是有一个空白的第一个元素。

这不是什么大不了的事情,而且我可以编写代码以在事实之后将其切断,但是我觉得我只是在制造某种语法错误,因为在我看来,默认的Rails行为并不是故意的没有任何理由添加这个空白元素。 我一定错过了某些东西,或者忘了禁用某种设置。 请帮助我了解我所缺less的东西(或者指出一些很好的文档,这些文档比我在intertubes上find的更深入)。

MySQL数据库表“模型”:

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

类模型包括以下设置:

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

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

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

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

  • 假定只select了value1和value3
  • "model" => { "subset_array" => ["", value1, value3] }

数据库更新如下所示:

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

正如你所看到的,数组中有这个额外的空白元素被发送和设置。 我该如何摆脱? 有没有我的f.select调用中缺less的参数?

非常感谢赞赏:)

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

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

隐藏的领域是什么造成的问题。 但是有一个很好的理由:当所有的值都被取消选中时,你仍然会收到一个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. 

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

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

在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 

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

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

 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 

这种方式可以在不接触模型层的情况下在控制器之间重复使用。

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

疑难杂症

HTML规范说未选中的checkbox或select不成功,因此Web浏览器不发送它们。 不幸的是,这引入了一个问题:如果一个发票模型有一个付费标志,并且在编辑付费发票的forms中,用户取消选中它的checkbox,则不发送付费参数。 所以,任何大规模的成语都是这样的

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

为了防止这种情况,助手会在checkbox之前生成一个辅助隐藏字段。 隐藏字段具有相同的名称,其属性模仿未选中的checkbox。

这样,客户端或者只发送隐藏的字段(代表checkbox未选中),或者两个字段。 由于HTML规范规定,键/值对必须以与表单中出现的顺序相同的顺序发送,并且参数提取将获取查询string中最后一次出现的重复键,这对于普通表单来说是有效的。

要删除空白值:

  def myfield=(value) value.reject!(&:blank?) write_attribute(:myfield, value) end 

在控制器中:

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

我在更新之前使用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 

我通过在页面的Javascript部分编写它来工作:

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

我看起来更像下面这样:

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

不知道这是否会让我头疼,但现在它工作正常。

使用jQuery:

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

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