This is a Rails gem for conveniently manage multiple nested models in a single form. It does so in an unobtrusive way through jQuery or Prototype.
This gem only works with Rails 3. See the rails2 branch for a plugin to work in Rails 2.
An example project showing how this works is available in the complex-nested-forms/nested_form branch.
Now you can override default behavior of inserting new subforms into your form (jQuery-only feature, sorry). For example:
window.nestedFormEvents.insertFields = function(content, assoc, link) { return $(link).closest('form').find(assoc + '_fields').append($(content)); }
There’s a small fix for Rails 3.1 form handling. Also support of Rails 3.1 led me to huge refactor of specs setup (now using Helper specs from rspec-rails instead of custom implementation spec context).
Formtastic is supported only with Rails 3.0, Rails 3.1 integration is not available now.
Asset pipeline is supported. So you can use
//= require jquery_nested_form // or //= require prototype_nested_form
Add it to your Gemfile then run bundle
to install it.
gem "nested_form"
Next run the generator to create the JavaScript file. This will automatically detect if you are using jQuery or Prototype.
rails g nested_form:install
Running the generator will add a file at public/javascripts/nested_form.js
which should be included after the jQuery or Prototype framework.
<%= javascript_include_tag :defaults, "nested_form" %>
Imagine you have a Project
model that has_many :tasks
. To be able to use this gem, you’ll need to add accepts_nested_attributes_for :tasks
to your Project model. If you don’t have the accepts_nested_attributes_for :tasks
you’ll get a Missing Block Error.
This will create a tasks_attributes=
method, so you may need to add it to the attr_accessible
array. (attr_accessible :tasks_attributes
)
Then use the nested_form_for
helper method to enable the nesting.
<%= nested_form_for @project do |f| %>
You will then be able to use link_to_add
and link_to_remove
helper methods on the form builder in combination with fields_for to dynamically add/remove nested records.
<%= f.fields_for :tasks do |task_form| %> <%= task_form.text_field :name %> <%= task_form.link_to_remove "Remove this task" %> <% end %> <p><%= f.link_to_add "Add a task", :tasks %></p>
Use simple_nested_form_for
or semantic_nested_form_for
for SimpleForm and Formtastic support respectively. This is feature is not yet in a Gem release but is in the Git repo.
It is often desirable to move the nested fields into a partial to keep things organized. If you don’t supply a block to fields_for it will look for a partial and use that.
<%= f.fields_for :tasks %>
In this case it will look for a partial called “task_fields” and pass the form builder as an f
variable to it.
If you are using jQuery, nested:fieldAdded
and nested:fieldRemoved
events are triggered on the form
element after adding and removing fields.
The default link_to_add action is to wrap the new fields in a div tag and insert that immediately before the link_to_add link element. In some cases, such as when appending to the bottom of a table, this will result in invalid HTML.
For example:
<table class="tasks"> <%= f.fields_for :tasks do |task_form| %> <td><%= task_form.text_field :name %></td> <td><%= task_form.link_to_remove "Remove this task" %></td> <% end %> <%# new fields should go here, wrapped in a tr element... %> </table> <p> <%# ...but instead they'll end up here wrapped in a div element %> <%= f.link_to_add "Add a task", :tasks %> </p>
To fix this, change the link_to_add line to:
<%= f.link_to_add "Add a task", :tasks, :container =>'table.tasks', :fields_element=>'tr' %>
This feature is currently only working with jQuery. Help getting the Prototype version working would be appreciated.
This gem was originally based on the solution by Tim Riley in his complex-form-examples fork.
Thank you Andrew Manshin for the Rails 3 transition, Andrea Singh for converting to a gem and Peter Giacomo Lombardo for Prototype support.
Andrea also wrote a great blog post on the internal workings of this gem.
Thanks Pavel Forkert for the SimpleForm and Formtastic support.