diff options
author | Frederick Cheung <frederick.cheung@gmail.com> | 2012-07-08 19:01:51 +0100 |
---|---|---|
committer | Frederick Cheung <frederick.cheung@gmail.com> | 2012-07-08 19:01:51 +0100 |
commit | 3cebfa49e1c9b03476b0f80dfe597a49e534fc33 (patch) | |
tree | c12bce853650464f089d2b4054b443eafd6389c2 /guides/source | |
parent | e68f63e6048aae61954904125b33e02a6c8934e5 (diff) | |
download | rails-3cebfa49e1c9b03476b0f80dfe597a49e534fc33.tar.gz rails-3cebfa49e1c9b03476b0f80dfe597a49e534fc33.tar.bz2 rails-3cebfa49e1c9b03476b0f80dfe597a49e534fc33.zip |
Document building complex forms using accepts_nested_attributes_for
Diffstat (limited to 'guides/source')
-rw-r--r-- | guides/source/form_helpers.textile | 135 |
1 files changed, 127 insertions, 8 deletions
diff --git a/guides/source/form_helpers.textile b/guides/source/form_helpers.textile index 1851aceff8..58338ce54b 100644 --- a/guides/source/form_helpers.textile +++ b/guides/source/form_helpers.textile @@ -10,7 +10,7 @@ In this guide you will: * Understand the date and time helpers Rails provides * Learn what makes a file upload form different * Learn some cases of building forms to external resources -* Find out where to look for complex forms +* Find out how to build complex forms endprologue. @@ -816,11 +816,130 @@ Or if you don't want to render an +authenticity_token+ field: h3. Building Complex Forms -Many apps grow beyond simple forms editing a single object. For example when creating a Person you might want to allow the user to (on the same form) create multiple address records (home, work, etc.). When later editing that person the user should be able to add, remove or amend addresses as necessary. While this guide has shown you all the pieces necessary to handle this, Rails does not yet have a standard end-to-end way of accomplishing this, but many have come up with viable approaches. These include: +Many apps grow beyond simple forms editing a single object. For example when creating a Person you might want to allow the user to (on the same form) create multiple address records (home, work, etc.). When later editing that person the user should be able to add, remove or amend addresses as necessary. -* As of Rails 2.3, Rails includes "Nested Attributes":./2_3_release_notes.html#nested-attributes and "Nested Object Forms":./2_3_release_notes.html#nested-object-forms -* Ryan Bates' series of Railscasts on "complex forms":http://railscasts.com/episodes/75 -* Handle Multiple Models in One Form from "Advanced Rails Recipes":http://media.pragprog.com/titles/fr_arr/multiple_models_one_form.pdf -* Eloy Duran's "complex-forms-examples":https://github.com/alloy/complex-form-examples/ application -* Lance Ivy's "nested_assignment":https://github.com/cainlevy/nested_assignment/tree/master plugin and "sample application":https://github.com/cainlevy/complex-form-examples/tree/cainlevy -* James Golick's "attribute_fu":https://github.com/jamesgolick/attribute_fu plugin +h4. Configuring the Model + +Active Record provides model level support via the +accepts_nested_attributes_for+ method: + +<ruby> +class Person < ActiveRecord::Base + has_many :addresses + accepts_nested_attributes_for :addresses + + attr_accessible :name, :addresses_attributes +end + +class Address < ActiveRecord::Base + belongs_to :person + attr_accessible :kind, :street +end +</ruby> + +This creates an +addresses_attributes=+ method on +Person+ that allows you to create, update and (optionally) destroy addresses. When using +attr_accessible+ or +attr_protected+ you must mark +addresses_attributes+ as accessible as well as the other attributes of +Person+ and +Address+ that should be mass assigned. + +h4. Building the Form + +The following form allows a user to create a +Person+ and its associated addresses. + +<erb> +<%= form_for @person do |f| %> + Addresses: + <ul> + <%= f.fields_for :addresses do |addresses_form| %> + <li> + <%= addresses_form.label :kind %> + <%= addresses_form.text_field :kind %> + + <%= addresses_form.label :street %> + <%= addresses_form.text_field :street %> + ... + </li> + <% end %> + </ul> +<% end %> +</erb> + + +When an association accepts nested attributes +fields_for+ renders its block once for every element of the association. In particular, if a person has no addresses it renders nothing. A common pattern is for the controller to build one or more empty children so that at least one set of fields is shown to the user. The example below would result in 3 sets of address fields being rendered on the new person form. + +<ruby> +def new + @person = Person.new + 3.times { @person.addresses.build} +end +</ruby> + ++fields_for+ yields a form builder that names parameters in the format expected the accessor generated by +accepts_nested_attributes_for+. For example when creating a user with 2 addresses, the submitted parameters would look like + +<ruby> +{ + :person => { + :name => 'John Doe', + :addresses_attributes => { + '0' => { + :kind => 'Home', + :street => '221b Baker Street', + }, + '1' => { + :kind => 'Office', + :street => '31 Spooner Street' + } + } + } +} +</ruby> + +The keys of the +:addresses_attributes+ hash are unimportant, they need merely be different for each address. + +If the associated object is already saved, +fields_for+ autogenerates a hidden input with the +id+ of the saved record. You can disable this by passing +:include_id => false+ to +fields_for+. You may wish to do this if the autogenerated input is placed in a location where an input tag is not valid HTML or when using an ORM where children do not have an id. + +h4. The Controller + +You do not need to write any specific controller code to use nested attributes. Create and update records as you would with a simple form. + +h4. Removing Objects + +You can allow users to delete associated objects by passing +allow_destroy => true+ to +accepts_nested_attributes_for+ + +<ruby> +class Person < ActiveRecord::Base + has_many :addresses + accepts_nested_attributes_for :addresses, :allow_destroy => true +end +</ruby> + +If the hash of attributes for an object contains the key +_destroy+ with a value of '1' or 'true' then the object will be destroyed. This form allows users to remove addresses: + +<erb> +<%= form_for @person do |f| %> + Addresses: + <ul> + <%= f.fields_for :addresses do |addresses_form| %> + <li> + <%= check_box :_destroy%> + <%= addresses_form.label :kind %> + <%= addresses_form.text_field :kind %> + ... + </li> + <% end %> + </ul> +<% end %> +</erb> + +h4. Preventing Empty Records + +It is often useful to ignore sets of fields that the user has not filled in. You can control this by passing a +:reject_if+ proc to +accepts_nested_attributes_for+. This proc will be called with each hash of attributes submitted by the form. If the proc returns +false+ then Active Record will not build an associated object for that hash. The example below only tries to build an address if the +kind+ attribute is set. + +<ruby> +class Person < ActiveRecord::Base + has_many :addresses + accepts_nested_attributes_for :addresses, :reject_if => lambda {|attributes| attributes['kind'].blank?} +end +</ruby> + +As a convenience you can instead pass the symbol +:all_blank+ which will create a proc that will reject records where all the attributes are blank excluding any value for +_destroy+. + +h4. Adding Fields on the Fly + +Rather than rendering multiple sets of fields ahead of time you may wish to add them only when a user clicks on an 'Add new child' button. Rails does not provide any builtin support for this. When generating new sets of fields you must ensure the the key of the associated array is unique - the current javascript date (milliseconds after the epoch) is a common choice.
\ No newline at end of file |