I’ve got a project called TellMe, which is a music release notification service. The app does pretty much exactly what I want it do, and now all I need to do is code up some user friendly UIs and deploy it for great victory. I’ve been keeping all of my models pretty thin, which means a lot of related models. This calls for nested forms.
The simplest example of this is the relation between my
ReleaseDate class contains information about when a
Release comes out, items that may be relevant for filtering, and is responsible for kicking off the daily release notification process.
I want it to be great. It should do something like this:
When you click ‘Add Release Date’, it should insert a new bullet above it for a new release date with all relevant information. How can we accomplish this?
The first step is to allow the
Release class to accept nested attributes in forms.
allow_destroy will allow us to destroy the object from the nested form. We want this. There’s another attribute called
update_only which prevents the nested form from creating new objects.
Second, the controller needs to be modified to allow these new parameters in:
params[:release_date_attributes[:_destroy]] evaluates to a truthy value, then the record will be marked for deletion. All of these parameters will get updated in a single transaction during
Cool! The back end of the app now works pretty much exactly like I want it to. It gets a params has with a bunch of release date information and updates/creates/destroys accordingly.
Rails is smart enough to do some pretty cool stuff behind-the-scenes with nested form objects thanks to the
accepts_nested_attributes_for method above. The
form_builder object has a method
fields_for which handles much of the hard work. My form code looks something like this at first:
Rails is, as usual, rather intelligent. It knows that Release has-many ReleaseDates and that
@release.release_dates is going to be an array. It will pull all of the objects out of the array and create those fields for each of them. If there aren’t any objects in the collection, then it won’t create anything. That is pretty cool! But it doesn’t let us create new ones – that’s why I’ve added the
First, I want the ‘Add Date’ link to work dynamically. I’ll add
remote: true to the options. This causes Rails to implement the link as an AJAX request rather than true link. Where will the link go? Since I’m creating a new
ReleaseDate associated with a
Release, I’ll make a route specifically for that:
release_dates_controller will need to set the release appropriately for the view. Lastly, I’ll need an actual
new.js.erb to implement the change.
Here are the changes:
Now, when I click on the link, the console gets a new message. So this is working exactly as I want it to so far!
First, I want to insert a new
li into the form. This will hold the form for the new field. jQuery makes this fairly easy:
This selects the
id='add-date', goes before it in the list, and inserts a new
class='release_date' and contents
hello!. So now, I just need to append the form to the
li and everything will be set, right? Unfortunately, while the following code looks right, it doesn’t quite work:
Clicking the link creates the new li and it has a form that looks pretty much right, but the data attributes aren’t correct, and the information doesn’t get included in the
release_date_attributes hash in the params.
Check out the resulting HTML: