ruby on rails f. sélectionner des options avec des attributs personnalisés

116

J'ai une instruction de sélection de formulaire, comme celle-ci:

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

Ce qui aboutit à ce code:

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

Mais je souhaite ajouter un attribut HTML personnalisé à mes options, comme ceci:

...
<option value="1" currency_code="XXX">Andorra</option>
<option value="2" currency_code="YYY">Argentina</option>
...
el_quick
la source
2
Rails ne fournit pas cette fonctionnalité, vous devrez créer un assistant pour créer ce balisage. N'oubliez pas non plus que l'exemple que vous avez mentionné n'est pas du HTML valide.
Augusto
Je sais, mon exemple n'est pas valide en html ... Je suppose que je dois changer ma manière d'obtenir les résultats que je veux, thk!
el_quick

Réponses:

356

Les rails PEUVENT ajouter des attributs personnalisés pour sélectionner des options, en utilisant l'assistant options_for_select existant. Vous aviez presque tout compris dans le code de votre question. Utilisation des attributs de données html5:

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

Ajout d'une sélection initiale:

<%= 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 vous avez besoin d'options groupées, vous pouvez utiliser l'assistant grouped_options_for_select, comme ceci (si @continents est un tableau d'objets continent, chacun ayant une méthode pays):

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

Le crédit devrait aller à paul @ pogodan qui a écrit sur le fait de trouver cela non pas dans la documentation, mais en lisant la source des rails. https://web.archive.org/web/20130128223827/http://www.pogodan.com/blog/2011/02/24/custom-html-attributes-in-options-for-select

Maison Anatortoise
la source
6

Vous pouvez le faire comme suit:

= f.select :country_id, @countries.map{ |c| [c.name, c.id, { 'data-currency-code' => c.currency_code} ] }
Nikhil Gupte
la source
Correct, mais déjà mentionné dans la réponse d'Anatortoise House.
Kelvin
La réponse acceptée n'illustre pas les attributs personnalisés utilisant ruby. Celui-ci fait donc je pense que c'est mieux car c'est la première réponse qui montre comment le faire via ruby.
Nikhil Gupte
5

Cela n'est pas possible directement avec Rails, et vous devrez créer votre propre assistant pour créer les attributs personnalisés. Cela dit, il existe probablement deux façons différentes d'accomplir ce que vous voulez:

(1) Utilisation d'un nom d'attribut personnalisé en HTML5. En HTML5, vous êtes autorisé à avoir des noms d'attributs personnalisés , mais ils doivent être précédés de «data-». Ces attributs personnalisés ne seront pas soumis avec votre formulaire, mais ils peuvent être utilisés pour accéder à vos éléments en Javascript. Si vous souhaitez accomplir cela, je vous recommande de créer un assistant qui génère des options comme celle-ci:

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

(2) Utilisation de valeurs avec fractionnement personnalisé pour soumettre des données supplémentaires. Si vous souhaitez réellement soumettre le code de devise, je vous recommande de créer votre boîte de sélection comme ceci:

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

Cela devrait générer du HTML qui ressemble à ceci:

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

Que vous pouvez ensuite analyser dans votre contrôleur:

@id, @currency_code = params[:country_id].split(':')
Pan Thomakos
la source
3
Très bonne réponse. J'ai opté pour l'approche numéro 1 et blogué comment j'ai créé l'aide au cas où cela aiderait quelqu'un d'autre. redguava.com.au/2011/03/…
Joel Friedlaender
D'accord avec les autres commentateurs - voir la réponse d'Anatortoise, ci-dessous!
Jacob
23
>>>>>>>>>>>>> Mauvaise réponse ... GARDER LE DÉFILEMENT <<<<<<<<<<<<<
stevenspiel
1
Ceci est directement possible dans Rails. Voir: stackoverflow.com/a/9076805/380607
Magne
Pan, veuillez supprimer cette réponse trompeuse.
vemv
4

Le hachage d'attributs supplémentaires n'est pris en charge que dans Rails 3.

Si vous êtes sur Rails 2.x et que vous souhaitez remplaceroptions_for_select

Je viens de copier le code de Rails 3. Vous devez remplacer ces 3 méthodes:

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 peu désordonné mais c'est une option. Je place ce code dans un module d'aide appelé RailsOverridesque j'inclus ensuite ApplicationHelper. Vous pouvez également créer un plugin / gem si vous préférez.

Un inconvénient est que pour tirer parti de ces méthodes, vous devez toujours appeler options_for_selectdirectement. Raccourcis comme

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

donnera les anciens résultats. Au lieu de cela, il devrait être:

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

Encore une fois, ce n'est pas une excellente solution, mais cela vaut peut-être la peine d'accéder à l'attribut data toujours aussi utile.

mastaBlasta
la source
0

J'ai également rencontré ce problème et j'ai créé le Ruby Gem "Enhanced_select" pour résoudre ce problème. Vous pouvez le trouver ici:

https://github.com/bkuhlmann/enhanced_select

Brooke Kuhlmann
la source
2
Ce lien vers une page 404 sur github.com
cevaris