简体   繁体   中英

How do I add HTML attributes to select options with Simple Form Rails?

I need to add a custom HTML attribute to each option for a select control. I'm using simple_form in Rails. Does anyone know how to do this? The attribute will be consumed by client-side JS.

For instance, I want to do something like this:

<%= f.input :group, collection: @groups, option_html: { data-type: lambda { |g| g[2] } } %>

Which would produce (simplified):

<select>
    <option value="1" data-type="primary">First Group</option>
    <option value="2" data-type="secondary">Second Group</option>
    <option value="3" data-type="secondary">Third Group</option>
</select>

Where @groups might look like this:

[
    ['First Group', 1, 'primary'],
    ['Second Group', 2, 'secondary'],
    ['Third Group', 3, 'secondary']
]

Hoping to avoid having to make a custom control/wrapper. Thanks!

You're close! easiest way is actually not using simple_form here. here's the simple_form documentation

<% options = @group.map { |g| [g.name, g.id, {'data-type' => g.group_type}] } %>
<%= f.input :group, label: 'Group' do %>
  <%= f.select :group, options, include_blank: 'Select a Group', class: 'form-control' %>
<% end %>

For your exact code it would be:

<% options = @group.map { |g| [g[0], g[1], {'data-type' => g[2]}] } %>
<%= f.input :group, label: 'Group' do %>
  <%= f.select :group, options, include_blank: 'Select a Group', class: 'form-control' %>
<% end %>

仅限简单形式:

= f.input :group, @groups.map{|l| [l[0], l[1], {data: {type: l[2]}}]}

A (small) drawback using f.input do end method is that any default input html options (like simple form's required or optional classes or the required attribute) and any default options (like b.use :input, class: 'input-element' ) are missing when simply passing a block to f.input , tldr: the input does not get decorated .

If you rely on these extra classes and attributes, you'd have to manually pass them in (not dry).

To overcome this I've created a custom input for my special selects, so I can define the body of my select like I want (the <option> tags) but the select gets decorated as usual:

# app/inputs/select_container_input.rb
class SelectContainerInput < SimpleForm::Inputs::Base 
  def input(wrapper_options)
    options_html = input_options.delete(:options_html)

    # since we pass our options by our self (and have to select the correct
    # option), set `selected` to `''` to prevent rails calling
    # `object.send(attribute_name)` only to set `selected` which is not used.
    input_options[:selected] = ''    

    merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
    @builder.select attribute_name, nil, input_options, merged_input_options do
      options_html
    end
  end
end

Simply call it like this:

<% options_html = capture do %>
  <option>bla</option>
<% end %>
<%= f.input :attribute, as: :select_container, options_html: options_html %>

The options_html is a workaround, because actually it would be easier to pass in a block to our custom input:

<%= f.input :attribute, as: :select_container do %>
  <option>bla</option>
<% end %>

But due to the way SimpleForm::FormBuilder#def_input works, the block gets carried away before code even touches the inputs. So no way without refactoring simple_form.

All in all this solves the problem with a little bit extra noisy code in your views for your special selects.

This seems to be the correct way of doing this:

Rails Simple Form custom association select field

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM