Rails 3 Nested Models in one form using Formtastic and Cocoon gems

This post shows how to handle nested models in one form with the parent model.

Let our models be projects and tasks:

– project with title and description;

– task has title;

– each project may have many tasks.

 

We want to create a form where we can edit project attributes and edit (add/remove/edit) multiple tasks for this project.

We will use Formtastic gem for building forms and Cocoon gem for making nested forms. Using these gems it is really easy to handle nested models.

Find the solution how to do it without using Formtastic and Cocoon gems – here.

 

Gems

Gemfile:

[codesyntax lang=”rails”]

gem 'formtastic'
gem 'cocoon'

[/codesyntax]

 

Models


# app/models/project.rb
class Project < ActiveRecord::Base
attr_accessible :title, :description, :tasks_attributes
has_many :tasks, :dependent => :destroy
accepts_nested_attributes_for :tasks, :reject_if => lambda { |a| a[:title].blank? }, :allow_destroy => true
..
end
# app/models/task.rb
class Task < ActiveRecord::Base
attr_accessible :project_id, :title
belongs_to :project
validates_presence_of :title
..
end
view raw models hosted with ❤ by GitHub


  Run this mysql script to create tables in a database:  
CREATE TABLE IF NOT EXISTS `projects` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ;
CREATE TABLE IF NOT EXISTS `tasks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`project_id` int(11) NOT NULL,
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ;


Javascript for Cocoon


# app/assets/javascripts/application.js
//= require cocoon
view raw js hosted with ❤ by GitHub


 

Views

Edit your form to edit parent object. Add this code to include nested objects (tasks):

[codesyntax lang=”rails”]

= f.inputs :name => “Tasks” do
= f.semantic_fields_for :tasks do |task|
= render “task_fields”, :f => task

.links
= link_to_add_association ‘Add task’, f, :tasks

[/codesyntax]    

 

So your form will look like this:  

# app/views/projects/_form.html.haml
= semantic_form_for @project do |f|
- if @project.errors.any?
#form_errors.error
= f.semantic_errors :state
%h2
#{pluralize(@item.errors.count, "error")}:
%ul
- @project.errors.full_messages.each do |msg|
%li
= msg
= f.inputs :name => "Basic" do
= f.input :title
= f.input :description, :required => false, :input_html => { :style => "width:500px; height:120px;" }, :rows=>4
= f.inputs :name => "Tasks" do
= f.semantic_fields_for :tasks do |task|
= render "task_fields", :f => task
.links
= link_to_add_association 'Add task', f, :tasks
.clear
%br
%br
.form-group
= f.actions do
= f.action :submit, :as => :button
= f.action :cancel, :as => :link, :label => "Cancel"
view raw form hosted with ❤ by GitHub

 

Partial view for task:


# app/views/project/_task_fields.html.haml
.nested-fields
= f.inputs do
= f.input :title
= link_to_remove_association "Remove", f


 

That’s it!

Your form should work so you can add new task to the project, edit existing tasks of the project or remove tasks in this form.

 

Issues

Error “Can’t mass-assign protected attributes”

You may get an error “Can’t mass-assign protected attributes: <nested_model_name>_attributes”.

In this case check that you have the following code in your parent model:

[codesyntax lang=”rails”]
class Project < ActiveRecord::Base
attr_accessible .., :tasks_attributes

has_many :tasks, :dependent => :destroy
accepts_nested_attributes_for :tasks, :reject_if => lambda { |a| a[:title].blank? }, :allow_destroy => true

..
end
[/codesyntax]

 

 

References:

* Tutorial – http://railscasts.com/episodes/196-nested-model-form-part-1

* https://github.com/ryanb/nested_form – gem for conveniently manage multiple nested models in a single form. It does so in an unobtrusive way through jQuery or Prototype.

* https://github.com/nathanvda/cocoon – Cocoon makes it easier to handle nested forms.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>