ruby on rails f. seleccionar opciones con atributos personalizados

115

Tengo una declaración de selección de formulario, como esta:

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

Lo que da como resultado este código:

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

Pero quiero agregar un atributo HTML personalizado a mis opciones, como este:

...
<option value="1" currency_code="XXX">Andorra</option>
<option value="2" currency_code="YYY">Argentina</option>
...
el_quick
fuente
2
Rails no proporciona esa funcionalidad, tendrá que crear un ayudante para crear ese marcado. Además, tenga en cuenta que el ejemplo que mencionó no es HTML válido.
Augusto
Lo sé, mi ejemplo no es HTML válido ... creo que tengo que cambiar mi forma de obtener los resultados que quiero, ¡gracias!
el_quick

Respuestas:

356

Rails PUEDE agregar atributos personalizados para seleccionar opciones, utilizando el ayudante options_for_select existente. Casi lo tienes bien en el código de tu pregunta. Usando atributos de datos html5:

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

Agregar una selección inicial:

<%= 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) %>

Si necesita opciones agrupadas, puede usar el ayudante grouped_options_for_select, así (si @continents es una matriz de objetos continentales, cada uno con un método de países):

<%= 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) %>

El crédito debería ser para paul @ pogodan, quien publicó sobre encontrar esto no en los documentos, sino leyendo la fuente de los rieles. https://web.archive.org/web/20130128223827/http://www.pogodan.com/blog/2011/02/24/custom-html-attributes-in-options-for-select

Casa Anatortoise
fuente
6

Puede hacer esto de la siguiente manera:

= f.select :country_id, @countries.map{ |c| [c.name, c.id, { 'data-currency-code' => c.currency_code} ] }
Nikhil Gupte
fuente
Correcto, pero ya mencionado en la respuesta de Anatortoise House.
Kelvin
La respuesta aceptada no ilustra los atributos personalizados usando ruby. Este lo hace, por lo tanto, creo que es mejor, ya que es la primera respuesta que muestra cómo hacerlo a través de ruby.
Nikhil Gupte
5

Esto no es posible directamente con Rails y tendrás que crear tu propio ayudante para crear los atributos personalizados. Dicho esto, probablemente haya dos formas diferentes de lograr lo que desea:

(1) Usar un nombre de atributo personalizado en HTML5. En HTML5 se le permite tener nombres de atributos personalizados , pero deben ir precedidos de 'datos-'. Estos atributos personalizados no se enviarán con su formulario, pero se pueden utilizar para acceder a sus elementos en Javascript. Si desea lograr esto, recomendaría crear un ayudante que genere opciones como esta:

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

(2) Usar valores con división personalizada para enviar datos adicionales. Si realmente desea enviar el código de moneda, le recomendaría crear su cuadro de selección como este:

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

Esto debería generar HTML con este aspecto:

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

Que luego puede analizar en su controlador:

@id, @currency_code = params[:country_id].split(':')
Pan Thomakos
fuente
3
Gran respuesta. Opté por el enfoque número 1 y escribí en un blog cómo hice el ayudante en caso de que ayude a alguien más. redguava.com.au/2011/03/…
Joel Friedlaender
De acuerdo con otros comentaristas: ¡vea la respuesta de Anatortoise a continuación!
Jacob
23
>>>>>>>>>>>>> RESPUESTA INCORRECTA ... SIGA
DESPLAZARSE
1
Esto es directamente posible en Rails. Ver: stackoverflow.com/a/9076805/380607
Magne
Pan, elimine esta respuesta engañosa.
vemv
4

El hash de atributos adicionales solo se admite en Rails 3.

Si está en Rails 2.xy desea anularoptions_for_select

Básicamente, solo copié el código de Rails 3. Debe anular estos 3 métodos:

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

Un poco desordenado, pero es una opción. Coloco este código en un módulo auxiliar llamado RailsOverridesque luego incluyo ApplicationHelper. También puede hacer un complemento / gema si lo prefiere.

Un problema es que para aprovechar estos métodos siempre debe invocar options_for_selectdirectamente. Atajos como

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

dará los resultados anteriores. En su lugar, debería ser:

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

Una vez más, no es una gran solución, pero valdría la pena llegar al siempre tan útil atributo de datos.

mastaBlasta
fuente
0

Me encontré con este problema también y creé la Gema Ruby "mejorado_selección" para resolver este problema. Lo puedes encontrar aquí:

https://github.com/bkuhlmann/enhanced_select

Brooke Kuhlmann
fuente
2
Esto enlaza a una página 404 en github.com
cevaris