125 votes

ruby on rails f.select options avec attributs personnalisés

J'ai une déclaration de sélection de formulaire, comme ceci :

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

Ce qui donne ce code :

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

Mais je veux 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>
...

2 votes

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.

0 votes

Je sais, mon exemple n'est pas valide en html... Je suppose que je dois changer ma façon de faire pour obtenir les résultats que je veux, merci !

372voto

Anatortoise House Points 2276

Rails peut ajouter des attributs personnalisés aux options de sélection, en utilisant l'aide existante options_for_select. Vous l'avez presque fait 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'aide grouped_options_for_select, comme ceci (si @continents est un tableau d'objets continent, chacun ayant une méthode countries) :

<%= 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 mérite en revient à paul @ pogodan qui a posté sur la découverte de ce problème non pas dans la documentation, mais en lisant les sources de rails. https://web.archive.org/web/20130128223827/http://www.pogodan.com/blog/2011/02/24/custom-html-attributes-in-options-for-select

6voto

Nikhil Gupte Points 1140

Vous pouvez procéder comme suit :

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

0 votes

Correct, mais déjà mentionné dans la réponse d'Anatortoise House.

0 votes

La réponse acceptée n'illustre pas les attributs personnalisés en utilisant ruby. Celle-ci le fait donc je pense qu'elle est meilleure car c'est la première réponse qui montre comment le faire via ruby.

5voto

Pan Thomakos Points 19946

Cela n'est pas possible directement avec Rails, et vous devrez créer votre propre aide 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 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. Pour ce faire, je vous recommande de créer une aide 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 voulez réellement soumettre le code de la 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(':')

3 votes

Excellente réponse. J'ai opté pour l'approche numéro 1 et j'ai raconté sur mon blog comment j'ai fabriqué l'assistant au cas où cela aiderait quelqu'un d'autre. redguava.com.au/2011/03/

0 votes

D'accord avec les autres commentateurs - voir la réponse d'Anatortoise, ci-dessous !

24 votes

>>>>>>>>>>>>>MAUVAISE RÉPONSE...CONTINUEZ À FAIRE DÉFILER LES PAGES<<<<<<<<<<<<

4voto

mastaBlasta Points 343

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 voulez passer outre options_for_select

J'ai simplement copié le code de Rails 3. Vous devez surcharger 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

C'est un peu compliqué mais c'est une option. Je place ce code dans un module d'aide appelé RailsOverrides que j'inclus ensuite dans ApplicationHelper . Vous pouvez aussi faire un plugin/gemme si vous préférez.

Un problème est que pour profiter de ces méthodes, vous devez toujours appeler options_for_select directement. Des 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, cela 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 solution idéale, mais cela peut valoir la peine d'accéder à l'attribut data, toujours aussi utile.

0voto

Brooke Kuhlmann Points 21

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

https://github.com/bkuhlmann/enhanced_select

2 votes

Ce lien renvoie à une page 404 sur github.com.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X