Sometimes a form edits a record together with its child records, like a movie with its showtimes. This lesson covers Rails' nested attributes and how to build and test such forms.
Important
Work on this lesson in
advisormode.
Learning goals
- You can explain how nested attributes let one form save a record together with its child records in a single transaction β all of it or nothing.
- You can explain how Rails form helpers name nested inputs and how those names turn into the nested structure of
params. - You can build a form that adds, edits and removes child records, ignores child forms that were left blank, and validates the rest.
- You can add child forms in the browser without a page load, using a JavaScript component that copies a template and adjusts the input names.
- You can write that JavaScript as a reusable component that knows nothing about a particular model.
- You can test a nested form, including how to address repeated fields.
- You can lay out a nested form so that child records are visually grouped and separated from the parent's fields.
Resources
Read what's new to you, skim what's familiar, skip what you already master. Stop when you can meet the learning goals.
Your agent can also generate an overview, a tutorial or an explanation for anything here, tailored to what you already know. Just ask.
- π
Rails Guide: Building complex forms
Show archive.org snapshot
β
accepts_nested_attributes_for,fields_for, how nested params are named - π Nested Forms in Rails Show archive.org snapshot β a worked example from model to view
- π Popular mistakes when using nested forms β our card
- π When aggregating nested children, always exclude children marked for destruction β our card
- π Unpoly + nested attributes in Rails: a short overview of different approaches β our card; adding and removing nested rows without a page load, from no-JS to dynamic templates
Exercises
Showtimes for MovieDB
- A movie has a list of showtimes. Each showtime consists of the name of the cinema, and the start datetime (both can be simple text fields).
- Expand the movie form with a simple interface that allows adding, editing and removing of showtimes.
- It is fine when you can only add 5 new showtimes at a time. Submitting the form will produce 5 new, blank showtime forms.
- Showtimes should validate the presence of cinema and start. However, when the form for a showtime has a blank cinema and start, that form should be ignored and no validation errors should be shown.
- Make sure to write tests for the showtimes form. How do you refer to the showtimes fields when there are multiple inputs for the date?
- Inspect your nested form and observe the
[name]attributes of your inputs. How does Rails assign the input data to the correct objects? - Style nested showtimes forms so showtimes are clearly separated from each other and from regular movie attributes. Avoid rendering a soup of text fields floating in an ocean of random margins.
Dynamic showtimes
Change the movie form so it no longer shows blank showtime fields for adding a new showtime. Instead there should be a button Add showtime that reveals a new blank showtime form.
The user can click on Add showtime as often as they like, always producing one more showtime form.
Implementation hint
- Render a single blank showtime form.
- A JavaScript component finds the blank form and hides it as a template for future showtimes.
- Clicking on Add showtime will copy the hidden template element, adjust
[name]and[id]attributes and append it to the DOM.
Component reuse
Package the Add showtime functionality as a generic JavaScript component so we can reuse it in other nested forms. For this to work the component must not contain any code or names specific to movies or the movie form.
Test that the component is reusable by allowing to add an actor's roles from within the actor form:
Name: [Al Porciono ]
ROLES:
+---------------------+-------------------------+------------+
| Movie | Character | |
+---------------------+-------------------------+------------|
| [The God Father v ] | [Michael Corleone ] | [ ] Remove |
| [Scarface v ] | [Tony montana ] | [ ] Remove |
| [Heat v ] | [Vincent Hanna ] | [ ] Remove |
|---------------------+-------------------------+------------+
| [Add role] |
+------------------------------------------------------------+
[ SAVE ]
Add a character (or casting) field to the Role model if you haven't already.
You do not need to implement authorization for the nested roles table. It is OK to create roles for any movie, even though you may not have write access to that movie.