In this screencast I show how to create nested forms. A nested form is a form within an other form. The aim is to generate a nicer user experience because the user doesn’t have to jump back and forth between parent and child forms. To show the effect I create a database of hotels which have different room categories. A hotel should be editable in one form which includes all the room categories.
We start a new Rails project with the name travel-agency and cd into that.
A scaffold for Hotel and RoomCategory
We need the following two scaffolds for this example project.
Because a hotel and a room category needs a name we add validation for that in the models. While we are there we add a to_s method which prints that name and add the needed associations.
Let’s include two example hotels with a couple of room categories in the seeds.
Start the Rails server
Reset the database which runs the seeds automatically and start Rails.
Hotel Show View
We want to list all room categories for a hotel in the show view of that hotel.
But that form doesn’t show any nested attributes. To achieve that we have to add accepts_nested_attributes_for in the hotel model.
Change the name of hotel categories
Any change within the nested attributes doesn’t work.
Let’s have a look at the console output while we update a hotel category name within the hotel form:
The room_categories_attributes are not in the controller strong parameters white list. This is a security feature of Rails 4 (have a look at the Strong Parameters plugin docu for more information about strong parameters).
Add New Room Categories in the Hotel Edit View
It probably would be useful to add an empty room category in the edit view just to be able to add one when ever you edit a hotel. For that we have to add a @hotel.room_categories.build into the edit method of the hotel controller.
But that triggers a side effect of our validation. Because of the name validation in the hotel_category model we can not save an empty name - it is just not valid. We can not change the validation but we can add a reject_if to the accepts_nested_attributes_for to reject an empty name attribute.
Now let’s add the same functionality to the hotel new form by adding @hotel.room_categories.build to the new method.
BTW: We could DRY this code by creating an after_filter for the edit and new method to run a @hotel.room_categories.build.
Destroy a Room Category in the Hotel Form
Now we are able to edit and create nested room categories in the hotel form. But we have no way to destroy a room category in that form. We need to add allow_destroy: true to the accepts_nested_attributes_for for that functionality.
For that to work we have to add a room_category.check_box :_destroy field in the hotel form.
And of course we have to add this attribute in the hotel controller strong parameter whitelist.
Now we can add, edit and remove room categories to in the hotel from.