Creates a scope around a specific model object like form_for, but doesn't create the form tags themselves. This makes #fields_for suitable for specifying additional model objects in the same form.
Although the usage and purpose of field_for
is similar to
form_for
's, its method signature is slightly different.
Like form_for
, it yields a FormBuilder object associated with a particular
model object to a block, and within the block allows methods to be called
on the builder to generate fields associated with the model object. Fields
may reflect a model object in two ways - how they are named (hence how
submitted values appear within the params
hash in the
controller) and what default values are shown when the form the fields
appear in is first displayed. In order for both of these features to be
specified independently, both an object name (represented by either a
symbol or string) and the object itself can be passed to the method
separately -
<%= form_for @person do |person_form| %> First name: <%= person_form.text_field :first_name %> Last name : <%= person_form.text_field :last_name %> <%= fields_for :permission, @person.permission do |permission_fields| %> Admin? : <%= permission_fields.check_box :admin %> <% end %> <%= person_form.submit %> <% end %>
In this case, the checkbox field will be represented by an HTML input
tag with the
name
attribute permission[admin]
, and the
submitted value will appear in the controller as
params[:permission][:admin]
. If
@person.permission
is an existing record with an attribute
admin
, the initial state of the checkbox when first displayed
will reflect the value of @person.permission.admin
.
Often this can be simplified by passing just the name of the model object
to fields_for
-
<%= fields_for :permission do |permission_fields| %> Admin?: <%= permission_fields.check_box :admin %> <% end %>
â¦in which case, if :permission
also happens to be the name of
an instance variable @permission
, the initial state of the
input field will reflect the value of that variable's attribute
@permission.admin
.
Alternatively, you can pass just the model object itself (if the first
argument isn't a string or symbol fields_for
will realize
that the name has been omitted) -
<%= fields_for @person.permission do |permission_fields| %> Admin?: <%= permission_fields.check_box :admin %> <% end %>
and fields_for
will derive the required name of the field from
the class of the model object, e.g. if
@person.permission
, is of class Permission
, the
field will still be named permission[admin]
.
Note: This also works for the methods in FormOptionHelper and DateHelper that are designed to work with an object as base, like FormOptionHelper#collection_select and ActionView::Helpers::DateHelper#datetime_select.
Nested Attributes Examples
When the object belonging to the current scope has a nested attribute writer for a certain attribute, #fields_for will yield a new scope for that attribute. This allows you to create forms that set or change the attributes of a parent object and its associations in one go.
Nested attribute writers are normal setter methods named after an
association. The most common way of defining these writers is either with
accepts_nested_attributes_for
in a model definition or by
defining a method with the proper name. For example: the attribute writer
for the association :address
is called
address_attributes=
.
Whether a one-to-one or one-to-many style form builder will be yielded depends on whether the normal reader method returns a single object or an array of objects.
One-to-one
Consider a Person class which returns a
single Address from the
address
reader method and responds to the
address_attributes=
writer method:
class Person def address @address end def address_attributes=(attributes) # Process the attributes hash end end
This model can now be used with a nested #fields_for, like so:
<%= form_for @person do |person_form| %> ... <%= person_form.fields_for :address do |address_fields| %> Street : <%= address_fields.text_field :street %> Zip code: <%= address_fields.text_field :zip_code %> <% end %> ... <% end %>
When address is already an association on a Person you can use
accepts_nested_attributes_for
to define the writer method for
you:
class Person < ActiveRecord::Base has_one :address accepts_nested_attributes_for :address end
If you want to destroy the associated model through the form, you have to
enable it first using the :allow_destroy
option for
accepts_nested_attributes_for
:
class Person < ActiveRecord::Base has_one :address accepts_nested_attributes_for :address, allow_destroy: true end
Now, when you use a form element with the _destroy
parameter,
with a value that evaluates to true
, you will destroy the
associated model (eg. 1, '1', true, or 'true'):
<%= form_for @person do |person_form| %> ... <%= person_form.fields_for :address do |address_fields| %> ... Delete: <%= address_fields.check_box :_destroy %> <% end %> ... <% end %>
One-to-many
Consider a Person class which returns an
array of Project instances from
the projects
reader method and responds to the
projects_attributes=
writer method:
class Person def projects [@project1, @project2] end def projects_attributes=(attributes) # Process the attributes hash end end
Note that the projects_attributes=
writer method is in fact
required for #fields_for
to correctly identify :projects
as a collection, and the
correct indices to be set in the form markup.
When projects is already an association on Person you can use
accepts_nested_attributes_for
to define the writer method for
you:
class Person < ActiveRecord::Base has_many :projects accepts_nested_attributes_for :projects end
This model can now be used with a nested fields_for. The block given to the nested #fields_for call will be repeated for each instance in the collection:
<%= form_for @person do |person_form| %> ... <%= person_form.fields_for :projects do |project_fields| %> <% if project_fields.object.active? %> Name: <%= project_fields.text_field :name %> <% end %> <% end %> ... <% end %>
It's also possible to specify the instance to be used:
<%= form_for @person do |person_form| %> ... <% @person.projects.each do |project| %> <% if project.active? %> <%= person_form.fields_for :projects, project do |project_fields| %> Name: <%= project_fields.text_field :name %> <% end %> <% end %> <% end %> ... <% end %>
Or a collection to be used:
<%= form_for @person do |person_form| %> ... <%= person_form.fields_for :projects, @active_projects do |project_fields| %> Name: <%= project_fields.text_field :name %> <% end %> ... <% end %>
If you want to destroy any of the associated models through the form, you
have to enable it first using the :allow_destroy
option for
accepts_nested_attributes_for
:
class Person < ActiveRecord::Base has_many :projects accepts_nested_attributes_for :projects, allow_destroy: true end
This will allow you to specify which models to destroy in the attributes
hash by adding a form element for the _destroy
parameter with
a value that evaluates to true
(eg. 1, '1', true, or
'true'):
<%= form_for @person do |person_form| %> ... <%= person_form.fields_for :projects do |project_fields| %> Delete: <%= project_fields.check_box :_destroy %> <% end %> ... <% end %>
When a collection is used you might want to know the index of each object
into the array. For this purpose, the index
method is
available in the FormBuilder object.
<%= form_for @person do |person_form| %> ... <%= person_form.fields_for :projects do |project_fields| %> Project #<%= project_fields.index %> ... <% end %> ... <% end %>
Note that #fields_for
will automatically generate a hidden field to store the ID of the record.
There are circumstances where this hidden field is not needed and you can
pass include_id: false
to prevent #fields_for from rendering
it automatically.
Please login to continue.