f.select具有自定义属性的选项


115

我有一个表单选择语句,像这样:

= f.select :country_id, @countries.map{ |c| [c.name, c.id] }

结果如下:

...
<option value="1">Andorra</option>
<option value="2">Argentina</option>
...

但是我想向选项中添加自定义HTML属性,如下所示:

...
<option value="1" currency_code="XXX">Andorra</option>
<option value="2" currency_code="YYY">Argentina</option>
...

2
Rails不提供该功能,您必须创建一个助手来创建该标记。另外,请记住,您提到的示例不是有效的HTML。
奥古斯托

我知道,我的示例不是有效的html ...我想我必须改变我的方式来获取我想要的结果,好吧!
el_quick

Answers:


356

Rails可以使用现有的options_for_select帮助程序添加自定义属性以选择选项。您在问题代码中几乎完全正确。使用html5数据属性:

<%= f.select :country_id, options_for_select(
    @countries.map{ |c| [c.name, c.id, {'data-currency_code'=>c.currency_code}] }) %>

添加初始选择:

<%= f.select :country_id, options_for_select(
    @countries.map{ |c| [c.name, c.id, {'data-currency_code'=>c.currency_code}] }, 
    selected_key = f.object.country_id) %>

如果需要分组选项,则可以使用grouped_options_for_select助手,例如(如果@continents是大洲对象的数组,每个对象都有一个countrys方法):

<%= f.select :country_id, grouped_options_for_select(
    @continents.map{ |group| [group.name, group.countries.
    map{ |c| [c.name, c.id, {'data-currency_code'=>c.currency_code}] } ] }, 
    selected_key = f.object.country_id) %>

应该归功于paul @ pogodan,他发布了有关在文档中找到此内容的信息,但通过阅读Rails来源发现了这一点。 https://web.archive.org/web/20130128223827/http://www.pogodan.com/blog/2011/02/24/custom-html-attributes-in-options-for-select


6

您可以按照以下步骤进行操作:

= f.select :country_id, @countries.map{ |c| [c.name, c.id, { 'data-currency-code' => c.currency_code} ] }

正确,但已经在Anatortoise House的答案中提及。
开尔文

接受的答案未说明使用ruby的自定义属性。因此,我确实感觉更好,因为这是显示如何通过红宝石进行操作的第一个答案。
Nikhil Gupte

5

使用Rails不可能直接做到这一点,并且您必须创建自己的帮助程序才能创建自定义属性。也就是说,可能有两种不同的方式来完成您想要的工作:

(1)在HTML5中使用自定义属性名称。在HTML5中,您可以使用自定义属性名称,但必须在名称前加上“ data-”。这些自定义属性不会与您的表单一起提交,但是可以用于访问Javascript中的元素。如果要完成此操作,建议您创建一个生成如下选项的助手:

<option value="1" data-currecy-code="XXX">Andorra</option>

(2)使用具有自定义拆分的值来提交其他数据。如果您实际上要提交货币代码,建议您创建一个如下所示的选择框:

= f.select :country_id, @countries.map{ |c| [c.name, "#{c.id}:#{c.currency_code}"] }

这应生成如下所示的HTML:

<option value="1:XXX">Andorra</option>
<option value="2:YYY">Argentina</option>

然后可以在控制器中进行解析:

@id, @currency_code = params[:country_id].split(':')

3
好答案。我选择了方法1,并在博客中写了如何制作帮助程序,以防其他人使用。redguava.com.au/2011/03/…–
Joel

同意其他评论者-参见下面的Anatortoise的答案!
2014年

23
>>>>>>>>>>>>>>>错误的答案...保持
滚动<<<<<<<<<<<

1
这在Rails中是直接可能的。请参阅:stackoverflow.com/a/9076805/380607
Magne

平移,请删除此误导性答案。
vemv '16

4

仅Rails 3支持额外的属性哈希。

如果您在Rails 2.x上,并且想要覆盖options_for_select

我基本上只是复制了Rails 3代码。您需要覆盖以下3种方法:

def options_for_select(container, selected = nil)
    return container if String === container
    container = container.to_a if Hash === container
    selected, disabled = extract_selected_and_disabled(selected)

    options_for_select = container.inject([]) do |options, element|
      html_attributes = option_html_attributes(element)
      text, value = option_text_and_value(element)
      selected_attribute = ' selected="selected"' if option_value_selected?(value, selected)
      disabled_attribute = ' disabled="disabled"' if disabled && option_value_selected?(value, disabled)
      options << %(<option value="#{html_escape(value.to_s)}"#{selected_attribute}#{disabled_attribute}#{html_attributes}>#{html_escape(text.to_s)}</option>)
    end

    options_for_select.join("\n").html_safe
end

def option_text_and_value(option)
  # Options are [text, value] pairs or strings used for both.
  case
  when Array === option
    option = option.reject { |e| Hash === e }
    [option.first, option.last]
  when !option.is_a?(String) && option.respond_to?(:first) && option.respond_to?(:last)
    [option.first, option.last]
  else
    [option, option]
  end
end

def option_html_attributes(element)
  return "" unless Array === element
  html_attributes = []
  element.select { |e| Hash === e }.reduce({}, :merge).each do |k, v|
    html_attributes << " #{k}=\"#{ERB::Util.html_escape(v.to_s)}\""
  end
  html_attributes.join
end

有点混乱,但这是一个选择。我将此代码放在一个名为的帮助程序模块中RailsOverrides,然后将其包含在中ApplicationHelper。您也可以根据需要制作插件/宝石。

一个陷阱是要利用这些方法,您必须始终options_for_select直接调用。快捷方式如

select("post", "person_id", Person.all.collect {|p| [ p.name, p.id, {"data-stuff"=>"html5"} ] })

将产生旧的结果。相反,它应该是:

select("post", "person_id", options_for_select(Person.all.collect {|p| [ p.name, p.id, {"data-stuff"=>"html5"} ] }))

同样,这不是一个很好的解决方案,但是值得获得如此有用的数据属性。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.