module ActionView::Helpers::FormOptionsHelper
def collection_check_boxes(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block)
In the rare case you don't want this hidden field, you can pass the
blank value.
This hidden field is given the same field name as the checkboxes with a
for every collection of checkboxes.
This is possible thanks to a hidden field generated by the helper method
will not be updated.
If no +category_ids+ are selected then we can safely assume this field
@user.update(params[:user])
have the following code in our update action:
For example, if we have a +User+ model with +category_ids+ field and we
web browsers will not send any value.
When no selection is made for a collection of checkboxes most
==== Gotcha
end
b.label(:"data-value" => b.value) { b.check_box + b.text }
collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
respectively. You can use them like this:
value, which are the current item being rendered, its text and value methods,
There are also three special methods available: object, text and
end
b.label(class: "check_box") { b.check_box(class: "check_box") }
collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
extra HTML options:
The builder methods label and check_box also accept
use the label as wrapper, as in the example above.
Using it, you can change the label and check box display order or even
for the current item in the collection, with proper text and value.
collection, which has the ability to generate the label and check box
The argument passed to the block is a special kind of builder for this
end
b.label { b.check_box }
collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
giving a block to the method:
It is also possible to customize the way the elements will be shown by
If @post.author_ids is already [1], this would return:
collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial)
Sample usage (selecting the associated Author for an instance of Post, @post):
end
end
"#{first_name.first}. #{last_name}"
def name_with_initial
has_and_belongs_to_many :posts
class Author < ActiveRecord::Base
end
has_and_belongs_to_many :authors
class Post < ActiveRecord::Base
Example object structure for use with this method:
retrieve the value/text.
as a +proc+, that will be called for each member of the +collection+ to
respectively. They can also be any object that responds to +call+, such
are used as the +value+ attribute and contents of each check box tag,
methods to be called on each member of +collection+. The return values
The :value_method and :text_method parameters are
+nil+, no selection is made.
on the instance +object+ will be selected. If calling +method+ returns
+method+ for +object+'s class. The value returned from calling +method+
Returns check box tags for the collection of existing return values of
def collection_check_boxes(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block) Tags::CollectionCheckBoxes.new(object, method, self, collection, value_method, text_method, options, html_options).render(&block) end
def collection_radio_buttons(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block)
In case if you don't want the helper to generate this hidden field you can specify
every collection of radio buttons. The hidden field has the same name as collection radio button and blank value.
To prevent this the helper generates an auxiliary hidden field before
will raise an error since no {user: ...} will be present.
params.require(:user).permit(...)
any strong parameters idiom like:
if a +User+ model has a +category_id+ field and in the form no category is selected, no +category_id+ parameter is sent. So,
Unfortunately this introduces a gotcha:
web browsers do not send any value to server.
The HTML specification says when nothing is selected on a collection of radio buttons
==== Gotcha
end
b.label(:"data-value" => b.value) { b.radio_button + b.text }
collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial) do |b|
respectively. You can use them like this:
value, which are the current item being rendered, its text and value methods,
There are also three special methods available: object, text and
end
b.label(class: "radio_button") { b.radio_button(class: "radio_button") }
collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial) do |b|
extra HTML options:
The builder methods label and radio_button also accept
even use the label as wrapper, as in the example above.
Using it, you can change the label and radio button display order or
for the current item in the collection, with proper text and value.
collection, which has the ability to generate the label and radio button
The argument passed to the block is a special kind of builder for this
end
b.label { b.radio_button }
collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial) do |b|
giving a block to the method:
It is also possible to customize the way the elements will be shown by
If @post.author_id is already 1, this would return:
collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial)
Sample usage (selecting the associated Author for an instance of Post, @post):
end
end
"#{first_name.first}. #{last_name}"
def name_with_initial
has_many :posts
class Author < ActiveRecord::Base
end
belongs_to :author
class Post < ActiveRecord::Base
Example object structure for use with this method:
retrieve the value/text.
as a +proc+, that will be called for each member of the +collection+ to
respectively. They can also be any object that responds to +call+, such
are used as the +value+ attribute and contents of each radio button tag,
methods to be called on each member of +collection+. The return values
The :value_method and :text_method parameters are
returns +nil+, no selection is made.
+method+ on the instance +object+ will be selected. If calling +method+
of +method+ for +object+'s class. The value returned from calling
Returns radio button tags for the collection of existing return values
def collection_radio_buttons(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block) Tags::CollectionRadioButtons.new(object, method, self, collection, value_method, text_method, options, html_options).render(&block) end
def collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})
def collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {}) Tags::CollectionSelect.new(object, method, self, collection, value_method, text_method, options, html_options).render end
def extract_selected_and_disabled(selected)
def extract_selected_and_disabled(selected) if selected.is_a?(Proc) [selected, nil] else selected = Array.wrap(selected) options = selected.extract_options!.symbolize_keys selected_items = options.fetch(:selected, selected) [selected_items, options[:disabled]] end end
def extract_values_from_collection(collection, value_method, selected)
def extract_values_from_collection(collection, value_method, selected) if selected.is_a?(Proc) collection.filter_map do |element| element.public_send(value_method) if selected.call(element) end else selected end end
def grouped_collection_select(object, method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
def grouped_collection_select(object, method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {}) Tags::GroupedCollectionSelect.new(object, method, self, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options).render end
def grouped_options_for_select(grouped_options, selected_key = nil, options = {})
Note: Only the
Possible output:
grouped_options_for_select(grouped_options, nil, divider: '---------')
]
['Denmark','Germany','France']
[['United States','US'], 'Canada'],
grouped_options = [
* :divider - the divider for the options groups.
prepends an option with a generic prompt - "Please select" - or the given prompt string.
* :prompt - set to true or a prompt string. When the select element doesn't have a value yet, this
Options:
as you might have the same option in multiple groups. Each will then get selected="selected".
which will have the +selected+ attribute set. Note: It is possible for this value to match multiple options
* +selected_key+ - A value equal to the +value+ attribute for one of the tags,
Ex. ["North America",[["United States","US"],["Canada","CA"]], { disabled: "disabled" }]
An optional third value can be provided as HTML attributes for the optgroup.
Ex. ["North America",[["United States","US"],["Canada","CA"]]]
nested array of text-value pairs. See options_for_select for more info.
* +grouped_options+ - Accepts a nested array or hash of strings. The first value serves as the
Parameters:
Possible output:
grouped_options_for_select(grouped_options)
}
'Europe' => ['Denmark','Germany','France']
'North America' => [['United States','US'], 'Canada'],
grouped_options = {
grouped_options_for_select(grouped_options)
]
['Denmark','Germany','France']]
['Europe',
[['United States','US'],'Canada']],
['North America',
grouped_options = [
wraps them with
Returns a string of tags, like options_for_select, but
def grouped_options_for_select(grouped_options, selected_key = nil, options = {}) prompt = options[:prompt] divider = options[:divider] body = "".html_safe if prompt body.safe_concat content_tag("option", prompt_text(prompt), value: "") end grouped_options.each do |container| html_attributes = option_html_attributes(container) if divider label = divider else label, container = container end html_attributes = { label: label }.merge!(html_attributes) body.safe_concat content_tag("optgroup", options_for_select(container, selected_key), html_attributes) end body end
def option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, selected_key = nil)
Note: Only the
...
...
Possible output:
option_groups_from_collection_for_select(@continents, :countries, :name, :id, :name, 3)
Sample usage:
end
# attribs: id, name, continent_id
belongs_to :continent
class Country < ActiveRecord::Base
end
# attribs: id, name
has_many :countries
class Continent < ActiveRecord::Base
Example object structure for use with this method:
to be specified.
to +option_key_method+. If +nil+, no selection is made. Can also be a hash if disabled values are
which will have the +selected+ attribute set. Corresponds to the return value of one of the calls
* +selected_key+ - A value equal to the +value+ attribute for one of the tags,
+collection+, returns a value to be used as the contents of its tag.
* +option_value_method+ - The name of a method which, when called on a child object of a member of
+collection+, returns a value to be used as the +value+ attribute for its tag.
* +option_key_method+ - The name of a method which, when called on a child object of a member of
string to be used as the +label+ attribute for its
* +group_label_method+ - The name of a method which, when called on a member of +collection+, returns a
array of child objects representing the tags.
* +group_method+ - The name of a method which, when called on a member of +collection+, returns an
* +collection+ - An array of objects representing the
Parameters:
groups them by
Returns a string of tags, like options_from_collection_for_select, but
def option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, selected_key = nil) collection.map do |group| option_tags = options_from_collection_for_select( value_for_collection(group, group_method), option_key_method, option_value_method, selected_key) content_tag("optgroup", option_tags, label: value_for_collection(group, group_label_method)) end.join.html_safe end
def option_html_attributes(element)
def option_html_attributes(element) if Array === element element.select { |e| Hash === e }.reduce({}, :merge!) else {} end end
def option_text_and_value(option)
def option_text_and_value(option) # Options are [text, value] pairs or strings used for both. if !option.is_a?(String) && option.respond_to?(:first) && option.respond_to?(:last) option = option.reject { |e| Hash === e } if Array === option [option.first, option.last] else [option, option] end end
def option_value_selected?(value, selected)
def option_value_selected?(value, selected) Array(selected).include? value end
def options_for_select(container, selected = nil)
# =>
# =>
# =>
# =>
options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], selected: "Free", disabled: "Super Platinum")
# =>
# =>
# =>
# =>
options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], disabled: ["Advanced", "Super Platinum"])
# =>
# =>
# =>
# =>
options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], disabled: "Super Platinum")
or array of values to be disabled. In this case, you can use :selected to specify selected option tags.
If you wish to specify disabled option tags, set +selected+ to be a hash, with :disabled being either a value
# =>
# =>
options_for_select([["Dollar", "$", { class: "bold" }], ["Kroner", "DKK", { onclick: "alert('HI');" }]])
# =>
# =>
# =>
options_for_select([ "Denmark", ["USA", { class: 'bold' }], "Sweden" ], ["USA", "Sweden"])
You can optionally provide HTML attributes as the last element of the array.
# =>
# =>
# =>
options_for_select([ "VISA", "MasterCard", "Discover" ], ["VISA", "Discover"])
# =>
# =>
options_for_select({ "Basic" => "$20", "Plus" => "$40" }, "$40")
# =>
# =>
options_for_select([ "VISA", "MasterCard" ], "MasterCard")
# =>
# =>
options_for_select([["Dollar", "$"], ["Kroner", "DKK"]])
may also be an array of values to be selected when using a multiple select.
become lasts. If +selected+ is specified, the matching "last" or element will get the selected option-tag. +selected+
the "firsts" as option text. Hashes are turned into this form automatically, so the keys become "firsts" and values
where the elements respond to first and last (such as a two-element array), the "lasts" serve as option values and
Accepts a container (hash, array, enumerable, your type) and returns a string of option tags. Given a container
def options_for_select(container, selected = nil) return container if String === container selected, disabled = extract_selected_and_disabled(selected).map do |r| Array(r).map(&:to_s) end container.map do |element| html_attributes = option_html_attributes(element) text, value = option_text_and_value(element).map(&:to_s) html_attributes[:selected] ||= option_value_selected?(value, selected) html_attributes[:disabled] ||= disabled && option_value_selected?(value, disabled) html_attributes[:value] = value tag_builder.content_tag_string(:option, text, html_attributes) end.join("\n").html_safe end
def options_from_collection_for_select(collection, value_method, text_method, selected = nil)
options_from_collection_for_select(@people, 'id', 'name', 1)
Will not select a person with the id of 1 because 1 (an Integer) is not the same as '1' (a string)
options_from_collection_for_select(@people, 'id', 'name', '1')
Failure to do this will produce undesired results. Example:
Be sure to specify the same class as the +value_method+ when specifying selected or disabled options.
+selected+ can also be a hash, specifying both :selected and/or :disabled values as required.
function are the selected values.
If +selected+ is specified as a Proc, those members of the collection that return true for the anonymous
will be selected option tag(s).
If +selected+ is specified as a value or array of values, the element(s) returning a match on +value_method+
select_tag 'person', options_from_collection_for_select(@people, 'id', 'name')
This is more often than not used inside a #select_tag like this example:
# =>
options_from_collection_for_select(@people, 'id', 'name')
the result of a call to the +value_method+ as the option value and the +text_method+ as the option text.
Returns a string of option tags that have been compiled by iterating over the +collection+ and assigning
def options_from_collection_for_select(collection, value_method, text_method, selected = nil) options = collection.map do |element| [value_for_collection(element, text_method), value_for_collection(element, value_method), option_html_attributes(element)] end selected, disabled = extract_selected_and_disabled(selected) select_deselect = { selected: extract_values_from_collection(collection, value_method, selected), disabled: extract_values_from_collection(collection, value_method, disabled) } options_for_select(options, select_deselect) end
def prompt_text(prompt)
def prompt_text(prompt) prompt.kind_of?(String) ? prompt : I18n.translate("helpers.select.prompt", default: "Please select") end
def select(object, method, choices = nil, options = {}, html_options = {}, &block)
In case if you don't want the helper to generate this hidden field you can specify
always contains a blank string.
the deselected multiple select box), or both fields. This means that the resulting array
Note: The client either sends only the hidden field (representing
every multiple select. The hidden field has the same name as multiple select and blank value.
To prevent this the helper generates an auxiliary hidden field before
wouldn't update roles.
@user.update(params[:user])
any mass-assignment idiom like
the user deselects all roles from +role_ids+ multiple select box, no +role_ids+ parameter is sent. So,
if a +User+ model has many +roles+ and have +role_ids+ accessor, and in the form that edits roles of the user
web browsers do not send any value to server. Unfortunately this introduces a gotcha:
The HTML specification says when +multiple+ parameter passed to select and all options got deselected
==== Gotcha
end
end
tag.option(c.name, value: c.id, data: { tags: c.tags.to_json })
available_campaigns.each do |c|
select(report, :campaign_ids) do
is useful when the options tag has complex attributes.
A block can be passed to +select+ to customize how the options tags will be rendered. This
tags by specifying the :disabled option. This can either be a single value or an array of values to be disabled.
or selected: nil to leave all options unselected. Similarly, you can specify values to be disabled in the option
By default, post.person_id is the selected option. Specify selected: value to use a different selection
In addition, this allows a single partial to be used to generate form inputs for both edit and create forms.
This allows the user to submit a form page more than once with the expected results of creating multiple records.
to the database. Instead, a second model object is created when the create request is received.
new model instance is assigned the default options and bound to @model_name. Usually this model is not saved
This can be used to provide a default set of options in the standard way: before rendering the create form, a
would become:
select :post, :person_id, Person.all.collect { |p| [ p.name, p.id ] }, { include_blank: true })
Example with @post.person_id => 2:
* A nested collection (see grouped_options_for_select).
* A flat collection (see options_for_select).
There are two possible formats for the +choices+ parameter, corresponding to other helpers' output:
The option currently held by the object will be selected, provided that the object is available.
Create a select tag and a series of contained option tags for the provided object and method.
def select(object, method, choices = nil, options = {}, html_options = {}, &block) Tags::Select.new(object, method, self, choices, options, html_options, &block).render end
def time_zone_options_for_select(selected = nil, priority_zones = nil, model = ::ActiveSupport::TimeZone)
NOTE: Only the option tags are returned, you have to wrap this call in
attempt to match the zones using
match?
method.zones; each object must respond to +name+. If a Regexp is given it will
must respond to +all+ and return an array of objects that represent time
be obtained in Active Record as a value object). The +model+ parameter
By default, +model+ is the ActiveSupport::TimeZone constant (which can
an ActiveSupport::TimeZone.
The +selected+ parameter must be either +nil+, or a string that names
of the US time zones, or a Regexp to select the zones of your choice)
ActiveSupport::TimeZone.us_zones as a convenience for obtaining a list
be listed above the rest of the (long) list. (You can use
ActiveSupport::TimeZone objects as +priority_zones+, so that they will
marked as the selected option tag. You can also supply an array of
world. Supply an ActiveSupport::TimeZone name as +selected+ to have it
Returns a string of option tags for pretty much any time zone in the
def time_zone_options_for_select(selected = nil, priority_zones = nil, model = ::ActiveSupport::TimeZone) zone_options = "".html_safe zones = model.all convert_zones = lambda { |list| list.map { |z| [ z.to_s, z.name ] } } if priority_zones if priority_zones.is_a?(Regexp) priority_zones = zones.select { |z| z.match?(priority_zones) } end zone_options.safe_concat options_for_select(convert_zones[priority_zones], selected) zone_options.safe_concat content_tag("option", "-------------", value: "", disabled: true) zone_options.safe_concat "\n" zones = zones - priority_zones end zone_options.safe_concat options_for_select(convert_zones[zones], selected) end
def time_zone_select(object, method, priority_zones = nil, options = {}, html_options = {})
time_zone_select(:user, :time_zone, /Australia/)
time_zone_select(:user, :time_zone, [ ActiveSupport::TimeZone["Alaska"], ActiveSupport::TimeZone["Hawaii"] ])
time_zone_select(:user, :time_zone, ActiveSupport::TimeZone.us_zones, default: "Pacific Time (US & Canada)")
time_zone_select(:user, :time_zone, nil, default: "Pacific Time (US & Canada)")
time_zone_select(:user, :time_zone, nil, include_blank: true)
a default ActiveSupport::TimeZone if the object's time zone is +nil+.
Finally, this method supports a :default option, which selects
your choice.
for another country's time zones, or a Regexp to select the zones of
of US time zones, ActiveSupport::TimeZone.country_zones(country_code)
(long) list. You can use ActiveSupport::TimeZone.us_zones for a list
as +priority_zones+ so that they will be listed above the rest of the
You can also supply an array of ActiveSupport::TimeZone objects
for more information.)
different time zone model object. (See +time_zone_options_for_select+
to ActiveSupport::TimeZone. This may be used by users to specify a
this method also supports a :model option, which defaults
In addition to the :include_blank option documented above,
#time_zone_options_for_select to generate the list of option tags.
Returns select and option tags for the given object and method, using
def time_zone_select(object, method, priority_zones = nil, options = {}, html_options = {}) Tags::TimeZoneSelect.new(object, method, self, priority_zones, options, html_options).render end
def value_for_collection(item, value)
def value_for_collection(item, value) value.respond_to?(:call) ? value.call(item) : item.public_send(value) end
def weekday_options_for_select(selected = nil, index_as_value: false, day_format: :day_names, beginning_of_week: Date.beginning_of_week)
NOTE: Only the option tags are returned, you have to wrap this call in
* :beginning_of_week - Defaults to Date.beginning_of_week.
Defaults to +:day_names+, set to +:abbr_day_names+ for abbreviations.
* :day_format - The I18n key of the array to use for the weekday options.
I18n.translate("date.day_names") as the values. By default, Sunday is always 0.
* :index_as_value - Defaults to false, set to true to use the indexes from
Options:
Returns a string of option tags for the days of the week.
def weekday_options_for_select(selected = nil, index_as_value: false, day_format: :day_names, beginning_of_week: Date.beginning_of_week) day_names = I18n.translate("date.#{day_format}") day_names = day_names.map.with_index.to_a if index_as_value day_names = day_names.rotate(Date::DAYS_INTO_WEEK.fetch(beginning_of_week)) options_for_select(day_names, selected) end
def weekday_select(object, method, options = {}, html_options = {}, &block)
Returns select and option tags for the given object and method, using
def weekday_select(object, method, options = {}, html_options = {}, &block) Tags::WeekdaySelect.new(object, method, self, options, html_options, &block).render end