ruby on rails f. seleziona le opzioni con attributi personalizzati


115

Ho una dichiarazione di selezione del modulo, come questa:

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

Il risultato è questo codice:

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

Ma voglio aggiungere un attributo HTML personalizzato alle mie opzioni, come questo:

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

2
Rails non fornisce questa funzionalità, dovrai creare un helper per creare quel markup. Inoltre, tieni presente che l'esempio che hai citato non è HTML valido.
Augusto

Lo so, il mio esempio non è valido html ... immagino di dover cambiare modo per ottenere i risultati che voglio, grazie!
el_quick

Risposte:


356

Rails PU aggiungere attributi personalizzati per selezionare le opzioni, usando l'helper options_for_select esistente. Hai quasi avuto ragione nel codice della tua domanda. Utilizzo degli attributi di dati html5:

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

Aggiunta di una selezione iniziale:

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

Se hai bisogno di opzioni raggruppate, puoi utilizzare l'helper grouped_options_for_select, in questo modo (se @continents è un array di oggetti continente, ciascuno con un metodo paesi):

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

Il merito dovrebbe andare a paul @ pogodan che ha postato sulla ricerca di questo non nei documenti, ma leggendo la fonte di rails. https://web.archive.org/web/20130128223827/http://www.pogodan.com/blog/2011/02/24/custom-html-attributes-in-options-for-select


6

Puoi farlo come segue:

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

Esatto, ma già menzionato nella risposta di Anatortoise House.
Kelvin

La risposta accettata non illustra gli attributi personalizzati utilizzando ruby. Questo fa quindi sento che è meglio in quanto è la prima risposta che mostra come farlo tramite ruby.
Nikhil Gupte

5

Questo non è possibile direttamente con Rails e dovrai creare il tuo helper per creare gli attributi personalizzati. Detto questo, ci sono probabilmente due modi diversi per ottenere ciò che desideri:

(1) Utilizzo di un nome di attributo personalizzato in HTML5. In HTML5 puoi avere nomi di attributi personalizzati , ma devono essere preceduti da "data-". Questi attributi personalizzati non verranno inviati con il modulo, ma possono essere utilizzati per accedere ai tuoi elementi in Javascript. Se vuoi farlo, ti consiglio di creare un helper che generi opzioni come questa:

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

(2) Utilizzo di valori con suddivisione personalizzata per inviare dati aggiuntivi. Se desideri effettivamente inviare il codice valuta, ti consiglio di creare la tua casella di selezione in questo modo:

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

Questo dovrebbe generare HTML simile a questo:

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

Che puoi quindi analizzare nel tuo controller:

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

3
Bella risposta. Ho optato per l'approccio numero 1 e ho scritto sul blog come ho creato l'helper nel caso in cui aiutasse qualcun altro. redguava.com.au/2011/03/…
Joel Friedlaender

D'accordo con altri commentatori - vedi la risposta di Anatortoise, sotto!
Jacob

23
>>>>>>>>>>>>> RISPOSTA SBAGLIATA ... CONTINUA A SCORRERE <<<<<<<<<<<<
stevenspiel

1
Questo è direttamente possibile in Rails. Vedi: stackoverflow.com/a/9076805/380607
Magne

Pan, per favore elimina questa risposta fuorviante.
vemv

4

L'hash degli attributi extra è supportato solo in Rails 3.

Se sei su Rails 2.x e vuoi sovrascrivereoptions_for_select

Fondamentalmente ho appena copiato il codice Rails 3. È necessario sovrascrivere questi 3 metodi:

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 po 'disordinato ma è un'opzione. Inserisco questo codice in un modulo helper chiamato RailsOverridesche poi includo in ApplicationHelper. Puoi anche fare un plugin / gem se preferisci.

Un trucco è che per sfruttare questi metodi devi sempre invocare options_for_selectdirettamente. Scorciatoie come

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

produrrà i vecchi risultati. Invece dovrebbe essere:

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

Anche in questo caso non è un'ottima soluzione, ma potrebbe valerne la pena per ottenere l'attributo di dati così utile.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.