Form Objects and Nesting with Cocoon
I created a Form Object (a Ruby class that inherits from ActiveModel::Model
) to add multiple records using a single form. I used Sam Slotsky’s Dynamically add nested forms post as a basis for my solution, but I wanted Cocoon to handle form nesting for my Rails 4.2 app.
Cocoon makes it trivial to add nested fields to your forms. However, it requires a model that accepts attributes for a child model via the ActiveRecord method accepts_nested_attributes_for
.
I’ll illustrate my solution using Sam’s Contact model. Below is the form object class from his post:
The changes I needed to make were simple. First, I followed Cocoon’s convention for the nested partials (ex. adding divs, naming the HTML classes after the associated model, etc). Secondly, I created an explicit initializer so that existing contacts are displayed as nested fields:
1
2
3
4
5
def initialize(attributes={})
super
set_instance_variables # variables needed to render the form or to save objects
@contacts ||= Contacts.where(...) # get the contacts based on the instance variables set above.
end
Line 4 fetches any existing database records for the Contact
model if the instance variable @contacts
was not set by the contacts_attributes=(attributes)
method shown in the earlier code block.
The
initialize
method callscontacts_attributes=(attributes)
if the controller passed the params corresponding to the nested fields, i.e. contacts_attributes in our example.
The third and last thing I needed to handle is the params[_destroy]
(see the Rails API) that Cocoon adds to indicate that a record should be destroyed. Here is my version of the contacts_attributes
method that allows for records to be destroyed:
Sam did not include a save
method in the post, so here is mine: