Ruby on rails f.выберите опции с настраиваемыми атрибутами
У меня есть оператор выбора формы, например:
= 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>
...
Ответы
Ответ 1
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 - это массив объектов континента, каждый из которых имеет метод стран):
<%= 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) %>
Следует отдать должное Полу Погодану, который написал о том, что это можно найти не в документации, а читая источник по рельсам. https://web.archive.org/web/20130128223827/http://www.pogodan.com/blog/2011/02/24/custom-html-attributes-in-options-for-select
Ответ 2
Это невозможно напрямую с Rails, и вам нужно создать собственный помощник для создания настраиваемых атрибутов. Тем не менее, существует, вероятно, два разных способа выполнить то, что вы хотите:
(1) Использование имени пользовательского атрибута в HTML5. В HTML5 вам разрешено иметь имена настраиваемых атрибутов, но они должны быть предварительно обработаны "данными". Эти пользовательские атрибуты не будут отправляться вместе с вашей формой, но они могут использоваться для доступа к вашим элементам в 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
Вы можете сделать это следующим образом:
= f.select :country_id, @countries.map{ |c| [c.name, c.id, { 'data-currency-code' => c.currency_code} ] }
Ответ 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"} ] }))
Опять же, отличное решение, но, возможно, стоит обратиться к столь полезному атрибуту данных.
Ответ 5
Я столкнулся с этой проблемой и создал "Ruby Gem" для улучшения этой проблемы. Вы можете найти его здесь:
https://github.com/bkuhlmann/enhanced_select