为什么在我的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();
从下拉菜单中删除空白选项的选项。