module ActionView::Helpers::FormHelper
def apply_form_for_options!(object_or_array, options) #:nodoc:
def apply_form_for_options!(object_or_array, options) #:nodoc: object = object_or_array.is_a?(Array) ? object_or_array.last : object_or_array object = convert_to_model(object) html_options = if object.respond_to?(:persisted?) && object.persisted? { :class => options[:as] ? "#{options[:as]}_edit" : dom_class(object, :edit), :id => options[:as] ? "#{options[:as]}_edit" : dom_id(object, :edit), :method => :put } else { :class => options[:as] ? "#{options[:as]}_new" : dom_class(object, :new), :id => options[:as] ? "#{options[:as]}_new" : dom_id(object), :method => :post } end options[:html] ||= {} options[:html].reverse_merge!(html_options) options[:url] ||= polymorphic_path(object_or_array) end
def check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0")
#
# =>
check_box("eula", "accepted", { :class => 'eula_check' }, "yes", "no")
#
# =>
check_box("puppy", "gooddog", {}, "yes", "no")
# Let's say that @puppy.gooddog is "no":
#
# =>
check_box("post", "validated")
# Let's say that @post.validated? is 1:
==== Examples
hashes instead of arrays.
In that case it is preferable to either use +check_box_tag+ or to use
get an extra ghost item with only that attribute, assigned to "0".
the elements of the array. For each item with a checked check box you
because parameter name repetition is precisely what Rails seeks to distinguish
<% end %>
...
<%= form.check_box :paid %>
<%= fields_for "project[invoice_attributes][]", invoice, :index => nil do |form| %>
within an array-like parameter, as in
Unfortunately that workaround does not work when the check box goes
key in the query string, that works for ordinary forms.
form, and parameters extraction gets the last occurrence of any repeated
says key/value pairs have to be sent in the same order they appear in the
the check box is unchecked), or both fields. Since the HTML specification
This way, the client either sends only the hidden field (representing
attributes mimic an unchecked check box.
the very check box. The hidden field has the same name and its
To prevent this the helper generates an auxiliary hidden field before
wouldn't update the flag.
@invoice.update_attributes(params[:invoice])
any mass-assignment idiom like
invoice the user unchecks its check box, no +paid+ parameter is sent. So,
if an +Invoice+ model has a +paid+ flag, and in the form that edits a paid
thus web browsers do not send them. Unfortunately this introduces a gotcha:
The HTML specification says unchecked check boxes are not successful, and
==== Gotcha
while the default +unchecked_value+ is set to 0 which is convenient for boolean values.
Additional options on the input tag can be passed as a hash with +options+. The +checked_value+ defaults to 1
It's intended that +method+ returns an integer and if that integer is above zero, then the checkbox is checked.
assigned to the template (identified by +object+). This object must be an instance object (@object) and not a local object.
Returns a checkbox tag tailored for accessing a specified attribute (identified by +method+) on an object
def check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0") InstanceTag.new(object_name, method, self, options.delete(:object)).to_check_box_tag(options, checked_value, unchecked_value) end
def email_field(object_name, method, options = {})
def email_field(object_name, method, options = {}) InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("email", options) end
def fields_for(record_or_name_or_array, *args, &block)
<% end %>
Delete: <%= project_fields.check_box :_destroy %>
<%= person_form.fields_for :projects do |project_fields| %>
...
<%= form_for @person do |person_form| %>
(eg. 1, '1', true, or 'true'):
parameter with a value that evaluates to +true+
attributes hash by adding a form element for the _destroy
This will allow you to specify which models to destroy in the
end
accepts_nested_attributes_for :projects, :allow_destroy => true
has_many :projects
class Person < ActiveRecord::Base
option for +accepts_nested_attributes_for+:
form, you have to enable it first using the :allow_destroy
If you want to destroy any of the associated models through the
end
accepts_nested_attributes_for :projects
has_many :projects
class Person < ActiveRecord::Base
+accepts_nested_attributes_for+ to define the writer method for you:
When projects is already an association on Person you can use
<% end %>
<% end %>
Name: <%= project_fields.text_field :name %>
<%= person_form.fields_for :projects, @active_projects do |project_fields| %>
...
<%= form_for @person do |person_form| %>
Or a collection to be used:
<% end %>
<% end %>
<% end %>
<% end %>
Name: <%= project_fields.text_field :name %>
<%= person_form.fields_for :projects, project do |project_fields| %>
<% if project.active? %>
<% @person.projects.each do |project| %>
...
<%= form_for @person do |person_form| %>
It's also possible to specify the instance to be used:
<% end %>
<% end %>
<% end %>
Name: <%= project_fields.text_field :name %>
<% if project_fields.object.active? %>
<%= person_form.fields_for :projects do |project_fields| %>
...
<%= form_for @person do |person_form| %>
collection:
the nested fields_for call will be repeated for each instance in the
This model can now be used with a nested fields_for. The block given to
end
end
# Process the attributes hash
def projects_attributes=(attributes)
end
[@project1, @project2]
def projects
class Person
projects_attributes= writer method:
from the projects reader method and responds to the
Consider a Person class which returns an _array_ of Project instances
==== One-to-many
<% end %>
<% end %>
Delete: <%= address_fields.check_box :_destroy %>
...
<%= person_form.fields_for :address do |address_fields| %>
...
<%= form_for @person do |person_form| %>
model (eg. 1, '1', true, or 'true'):
with a value that evaluates to +true+, you will destroy the associated
Now, when you use a form element with the _destroy parameter,
end
accepts_nested_attributes_for :address, :allow_destroy => true
has_one :address
class Person < ActiveRecord::Base
+accepts_nested_attributes_for+:
to enable it first using the :allow_destroy option for
If you want to destroy the associated model through the form, you have
end
accepts_nested_attributes_for :address
has_one :address
class Person < ActiveRecord::Base
+accepts_nested_attributes_for+ to define the writer method for you:
When address is already an association on a Person you can use
<% end %>
<% end %>
Zip code: <%= address_fields.text_field :zip_code %>
Street : <%= address_fields.text_field :street %>
<%= person_form.fields_for :address do |address_fields| %>
...
<%= form_for @person do |person_form| %>
This model can now be used with a nested fields_for, like so:
end
end
# Process the attributes hash
def address_attributes=(attributes)
end
@address
def address
class Person
address_attributes= writer method:
address reader method and responds to the
Consider a Person class which returns a _single_ Address from the
==== One-to-one
or an _array_ of objects.
depends on whether the normal reader method returns a _single_ object
Whether a one-to-one or one-to-many style form builder will be yielded
address_attributes=.
writer for the association :address is called
defining a method with the proper name. For example: the attribute
with +accepts_nested_attributes_for+ in a model definition or by
association. The most common way of defining these writers is either
Nested attribute writers are normal setter methods named after an
the attributes of a parent object and its associations in one go.
for that attribute. This allows you to create forms that set or change
writer for a certain attribute, fields_for will yield a new scope
When the object belonging to the current scope has a nested attribute
=== Nested Attributes Examples
FormOptionHelper#collection_select and DateHelper#datetime_select.
DateHelper that are designed to work with an object as base, like
Note: This also works for the methods in FormOptionHelper and
<% end %>
Admin?: <%= permission_fields.check_box :admin %>
<%= fields_for :person do |permission_fields| %>
...or if you don't have an object, just a name of the parameter:
<% end %>
Admin?: <%= permission_fields.check_box :admin %>
<%= fields_for :person, @client do |permission_fields| %>
parameter, like a Client that acts as a Person:
...or if you have an object that needs to be represented as a different
<% end %>
<% end %>
Admin? : <%= permission_fields.check_box :admin %>
<%= fields_for @person.permission do |permission_fields| %>
Last name : <%= person_form.text_field :last_name %>
First name: <%= person_form.text_field :first_name %>
<%= form_for @person do |person_form| %>
=== Generic Examples
for specifying additional model objects in the same form.
doesn't create the form tags themselves. This makes fields_for suitable
Creates a scope around a specific model object like form_for, but
def fields_for(record_or_name_or_array, *args, &block) raise ArgumentError, "Missing block" unless block_given? options = args.extract_options! case record_or_name_or_array when String, Symbol object_name = record_or_name_or_array object = args.first else object = record_or_name_or_array object_name = ActiveModel::Naming.singular(object) end builder = options[:builder] || ActionView::Base.default_form_builder capture(builder.new(object_name, object, self, options, block), &block) end
def file_field(object_name, method, options = {})
# =>
file_field(:attachment, :file, :class => 'file_input')
# =>
file_field(:post, :attached, :accept => 'text/html')
# =>
file_field(:user, :avatar)
==== Examples
shown.
hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
Returns an file upload input tag tailored for accessing a specified attribute (identified by +method+) on an object
def file_field(object_name, method, options = {}) InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("file", options.update({:size => nil})) end
def form_for(record_or_name_or_array, *args, &proc)
If you don't need to attach a form to a model instance, then check out
end
form_for(record_or_name_or_array, *(args << options.merge(:builder => LabellingFormBuilder)), &proc)
options = args.extract_options!
def labelled_form_for(record_or_name_or_array, *args, &proc)
could do something like the following:
In many cases you will want to wrap the above in another helper, so you
of a nested fields_for call, unless it's explicitly set.
The custom FormBuilder class is automatically merged with the options
labelling_form.
variable referencing the form builder is called
The rendered template is people/_labelling_form and the local
<%= render :partial => f %>
In this case, if you use this:
<% end %>
<%= check_box_tag "person[admin]", @person.company.admin? %>
<%= text_area :person, :biography %>
<%= f.text_field :last_name %>
<%= f.text_field :first_name %>
<%= form_for @person, :url => { :action => "create" }, :builder => LabellingFormBuilder do |f| %>
automatically add labels to form inputs.
custom builder. For example, let's say you made a helper to
FormBuilder and override or define some more helpers, then use your
You can also build forms using a customized FormBuilder class. Subclass
=== Customized form builders
...
def form_for(record_or_name_or_array, *args, &proc) raise ArgumentError, "Missing block" unless block_given? options = args.extract_options! case record_or_name_or_array when String, Symbol ActiveSupport::Deprecation.warn("Using form_for(:name, @resource) is deprecated. Please use form_for(@resource, :as => :name) instead.", caller) unless args.empty? object_name = record_or_name_or_array when Array object = record_or_name_or_array.last object_name = options[:as] || ActiveModel::Naming.singular(object) apply_form_for_options!(record_or_name_or_array, options) args.unshift object else object = record_or_name_or_array object_name = options[:as] || ActiveModel::Naming.singular(object) apply_form_for_options!([object], options) args.unshift object end (options[:html] ||= {})[:remote] = true if options.delete(:remote) output = form_tag(options.delete(:url) || {}, options.delete(:html) || {}) output << fields_for(object_name, *(args << options), &proc) output.safe_concat('</form>') end
def hidden_field(object_name, method, options = {})
hidden_field(:user, :token)
# =>
hidden_field(:post, :tag_list)
# =>
hidden_field(:signup, :pass_confirm)
==== Examples
shown.
hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
Returns a hidden input tag tailored for accessing a specified attribute (identified by +method+) on an object
def hidden_field(object_name, method, options = {}) InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("hidden", options) end
def label(object_name, method, content_or_options = nil, options = nil, &block)
'Accept Terms.'
label(:post, :terms) do
# =>
label(:post, :privacy, "Public Post", :value => "public")
# =>
label(:post, :title, "A short title", :class => "title_label")
# =>
label(:post, :title, "A short title")
# =>
label(:post, :cost)
cost: "Total cost"
post:
attribute:
activemodel:
Localization can also be based purely on the translation of the attribute-name like this:
# =>
label(:post, :body)
Which then will result in
body: "Write your entire text here"
post:
label:
helpers:
For example you can define the following in your locale (e.g. en.yml)
You can localize your labels based on model and attribute names.
# =>
label(:post, :title)
==== Examples
target labels for radio_button tags (where the value is used in the ID of the input tag).
onto the HTML as an HTML element attribute as in the example shown, except for the :value option, which is designed to
Additional options on the label tag can be passed as a hash with +options+. These options will be tagged
is found in the current I18n locale (through helpers.label.
assigned to the template (identified by +object+). The text of label will default to the attribute name unless a translation
Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
def label(object_name, method, content_or_options = nil, options = nil, &block) content_is_options = content_or_options.is_a?(Hash) if content_is_options || block_given? options = content_or_options if content_is_options text = nil else text = content_or_options end options ||= {} InstanceTag.new(object_name, method, self, options.delete(:object)).to_label_tag(text, options, &block) end
def number_field(object_name, method, options = {})
==== Options
Returns an input tag of type "number".
def number_field(object_name, method, options = {}) InstanceTag.new(object_name, method, self, options.delete(:object)).to_number_field_tag("number", options) end
def password_field(object_name, method, options = {})
# =>
password_field(:account, :pin, :size => 20, :class => 'form_input')
# =>
password_field(:user, :password, :onchange => "if $('user[password]').length > 30 { alert('Your password needs to be shorter!'); }")
# =>
password_field(:account, :secret, :class => "form_input")
# =>
password_field(:login, :pass, :size => 20)
==== Examples
shown.
hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
Returns an input tag of the "password" type tailored for accessing a specified attribute (identified by +method+) on an object
def password_field(object_name, method, options = {}) InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("password", options) end
def radio_button(object_name, method, tag_value, options = {})
# =>
radio_button("user", "receive_newsletter", "no")
radio_button("user", "receive_newsletter", "yes")
#
# =>
radio_button("post", "category", "java")
radio_button("post", "category", "rails")
# Let's say that @post.category returns "rails":
==== Examples
+options+ hash. You may pass HTML options there as well.
To force the radio button to be checked pass :checked => true in the
radio button will be checked.
assigned to the template (identified by +object+). If the current value of +method+ is +tag_value+ the
Returns a radio button tag for accessing a specified attribute (identified by +method+) on an object
def radio_button(object_name, method, tag_value, options = {}) InstanceTag.new(object_name, method, self, options.delete(:object)).to_radio_button_tag(tag_value, options) end
def range_field(object_name, method, options = {})
==== Options
Returns an input tag of type "range".
def range_field(object_name, method, options = {}) InstanceTag.new(object_name, method, self, options.delete(:object)).to_number_field_tag("range", options) end
def search_field(object_name, method, options = {})
def search_field(object_name, method, options = {}) options = options.stringify_keys if options["autosave"] if options["autosave"] == true options["autosave"] = request.host.split(".").reverse.join(".") end options["results"] ||= 10 end if options["onsearch"] options["incremental"] = true unless options.has_key?("incremental") end InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("search", options) end
def telephone_field(object_name, method, options = {})
def telephone_field(object_name, method, options = {}) InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("tel", options) end
def text_area(object_name, method, options = {})
# #{@entry.body}
# =>
# #{@application.notes}
# =>
# #{@comment.text}
# =>
# #{@post.body}
# =>
def text_area(object_name, method, options = {}) InstanceTag.new(object_name, method, self, options.delete(:object)).to_text_area_tag(options) end
def text_field(object_name, method, options = {})
# =>
text_field(:snippet, :code, :size => 20, :class => 'code_input')
# =>
text_field(:session, :user, :onchange => "if $('session[user]').value == 'admin' { alert('Your login can not be admin!'); }")
# =>
text_field(:post, :title, :class => "create_input")
# =>
text_field(:post, :title, :size => 20)
==== Examples
shown.
hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
Returns an input tag of the "text" type tailored for accessing a specified attribute (identified by +method+) on an object
def text_field(object_name, method, options = {}) InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("text", options) end
def url_field(object_name, method, options = {})
def url_field(object_name, method, options = {}) InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("url", options) end