具有自定义属性的 select 选项

我有一个表单选择语句,如下所示:

= 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>
...
103818 次浏览

这在 Rails 中是不可能直接实现的,您必须创建自己的助手来创建自定义属性。也就是说,可能有两种不同的方式来实现你想要的:

(1) Using a custom attribute name in HTML5.在 HTML5中允许使用 custom attribute names,但它们必须在前面加上“ 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(':')

我也遇到了这个问题,并创建了“  增强的 _ select”RubyGem 以解決此問題。你可以在这里找到:

Https://github.com/bkuhlmann/enhanced_select

Rails CAN 使用现有的 options _ for _ select helper 为选项添加自定义属性。你的问题中的代码几乎是正确的。使用 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) %>

如果需要分组选项,可以使用 group _ options _ for _ select helper,如下所示(如果@Continental 是一个大陆对象数组,每个对象都有一个 country 方法) :

<%= 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,他没有在文档中发现这一点,而是通过阅读 Railssource 发现了这一点。https://web.archive.org/web/20130128223827/http://www.pogodan.com/blog/2011/02/24/custom-html-attributes-in-options-for-select

你可以这样做:

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

额外的属性散列只在 Rails 3中支持。

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

I basically just copied the Rails 3 code. You need to override these 3 methods:

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中。如果愿意,也可以使用插件/gem。

一个问题是,要利用这些方法,您必须始终直接调用 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"} ] }))

同样不是一个很好的解决方案,但是为了获得非常有用的 data-Attribute,它可能是值得的。