рубин на рельсах f. выбор параметров с настраиваемыми атрибутами

116

У меня есть оператор выбора формы, например:

= 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>
...
el_quick
источник
2
Rails не предоставляет такой функциональности, вам придется создать помощник для создания этой разметки. Также имейте в виду, что приведенный вами пример не является допустимым HTML.
Августо
Я знаю, что мой пример недействителен ... Думаю, мне нужно изменить свой способ, чтобы получить желаемые результаты, спасибо!
el_quick

Ответы:

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 - это массив объектов континентов, каждый из которых имеет метод стран):

<%= 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.
Кельвин
В принятом ответе не показаны настраиваемые атрибуты с использованием рубина. Это делает, поэтому я считаю, что это лучше, поскольку это первый ответ, который показывает, как это сделать с помощью рубина.
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 Friedlaender
Согласитесь с другими комментаторами - см. Ответ Анатортуаза ниже!
Джейкоб
23
>>>>>>>>>>>>> НЕПРАВИЛЬНЫЙ ОТВЕТ ... ПРОДОЛЖАЙТЕ
ПРОКРУТКУ
1
Это напрямую возможно в Rails. См .: stackoverflow.com/a/9076805/380607
Magne
Пан, пожалуйста, удали этот вводящий в заблуждение ответ.
vemv
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"} ] }))

Опять же, не лучшее решение, но, возможно, стоит добраться до столь полезного атрибута данных.

mastaBlasta
источник
0

Я тоже столкнулся с этой проблемой и создал Ruby Gem «расширенный_выбор», чтобы решить эту проблему. Вы можете найти это здесь:

https://github.com/bkuhlmann/enhanced_select

Брук Кульман
источник
2
Это ссылка на страницу 404 на github.com
cevaris