ruby on rails f.select opcje z niestandardowymi atrybutami

115

Mam formularz wyboru oświadczenia, taki jak ten:

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

Co daje ten kod:

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

Ale chcę dodać niestandardowy atrybut HTML do moich opcji, na przykład:

...
<option value="1" currency_code="XXX">Andorra</option>
<option value="2" currency_code="YYY">Argentina</option>
...
el_quick
źródło
2
Railsy nie zapewniają takiej funkcjonalności, będziesz musiał stworzyć pomocnika do stworzenia tego znacznika. Pamiętaj też, że podany przykład nie jest poprawnym kodem HTML.
Augusto
Wiem, mój przykład nie jest poprawnym html ... Chyba muszę zmienić sposób, aby uzyskać wyniki, które chcę, dzięki!
el_quick,

Odpowiedzi:

356

Railsy MOGĄ dodawać niestandardowe atrybuty do wybierania opcji, używając istniejącego helpera options_for_select. Prawie udało Ci się to znaleźć w kodzie pytania. Korzystanie z atrybutów danych html5:

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

Dodanie wstępnej selekcji:

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

Jeśli potrzebujesz zgrupowanych opcji, możesz użyć pomocnika grouped_options_for_select, w ten sposób (jeśli @continents jest tablicą obiektów kontynentów, z których każdy ma metodę krajów):

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

Podziękowania należy się paul @ pogodan, który opublikował informację o znalezieniu tego nie w dokumentach, ale czytając źródło rails. https://web.archive.org/web/20130128223827/http://www.pogodan.com/blog/2011/02/24/custom-html-attributes-in-options-for-select

Dom Anatortoise'a
źródło
6

Możesz to zrobić w następujący sposób:

= f.select :country_id, @countries.map{ |c| [c.name, c.id, { 'data-currency-code' => c.currency_code} ] }
Nikhil Gupte
źródło
Prawidłowo, ale już wspomniano w odpowiedzi Anatortoise House.
Kelvin
Zaakceptowana odpowiedź nie ilustruje niestandardowych atrybutów używających ruby. Ten robi, więc czuję, że jest lepszy, ponieważ jest to pierwsza odpowiedź, która pokazuje, jak to zrobić za pomocą ruby.
Nikhil Gupte
5

Nie jest to możliwe bezpośrednio w Railsach i będziesz musiał stworzyć własnego pomocnika do tworzenia niestandardowych atrybutów. To powiedziawszy, prawdopodobnie istnieją dwa różne sposoby osiągnięcia tego, co chcesz:

(1) Używanie niestandardowej nazwy atrybutu w HTML5. W HTML5 możesz mieć niestandardowe nazwy atrybutów , ale muszą one być poprzedzone znakiem „data-”. Te atrybuty niestandardowe nie zostaną przesłane wraz z formularzem, ale można ich użyć do uzyskania dostępu do elementów w JavaScript. Jeśli chcesz to osiągnąć, polecam stworzenie pomocnika, który generuje takie opcje:

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

(2) Używanie wartości z niestandardowym podziałem do przesyłania dodatkowych danych. Jeśli faktycznie chcesz przesłać kod waluty, polecam utworzenie pola wyboru w następujący sposób:

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

Powinno to wygenerować kod HTML, który wygląda następująco:

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

Które możesz następnie przeanalizować w kontrolerze:

@id, @currency_code = params[:country_id].split(':')
Pan Thomakos
źródło
3
Świetna odpowiedź. Zdecydowałem się na podejście numer 1 i napisałem na blogu, jak zrobiłem pomocnika na wypadek, gdyby pomogło to komukolwiek innemu. redguava.com.au/2011/03/…
Joel Friedlaender
Zgadzam się z innymi komentatorami - zobacz odpowiedź Anatortoise'a poniżej!
Jacob,
23
>>>>>>>>>>>>> ZŁA ODPOWIEDŹ ... KEEP SCROLLING <<<<<<<<<<<<<
stevenspiel
1
Jest to bezpośrednio możliwe w Railsach. Zobacz: stackoverflow.com/a/9076805/380607
Magne
Pan, usuń tę mylącą odpowiedź.
vemv
4

Funkcja mieszania dodatkowych atrybutów jest obsługiwana tylko w Railsach 3.

Jeśli korzystasz z Rails 2.x i chcesz przesłonićoptions_for_select

Po prostu skopiowałem kod Rails 3. Musisz zastąpić te 3 metody:

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

Trochę niechlujne, ale to opcja. Umieszczam ten kod w module pomocniczym o nazwie, RailsOverridesktóry następnie dołączam do ApplicationHelper. Jeśli wolisz, możesz także zrobić wtyczkę / klejnot.

Jedna trudność polega na tym, że aby skorzystać z tych metod, należy zawsze wywoływać options_for_selectbezpośrednio. Skróty takie jak

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

przyniesie stare rezultaty. Zamiast tego powinno być:

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

Ponownie nie jest to świetne rozwiązanie, ale warto sięgnąć do zawsze tak użytecznego atrybutu danych.

mastaBlasta
źródło