Improve fields_for helper sections [ci-skip]

Prior to this commit, there were two sections in the Form Helpers guide
with the same heading "The `fields_for` Helper".

This commit differentiates those two headings, and makes some minor
improvements to both sections for clarity.
This commit is contained in:
Jonathan Hefner 2022-02-10 13:44:16 -06:00
parent 73e0a9c749
commit 031f7af1d6
1 changed files with 51 additions and 22 deletions

View File

@ -238,7 +238,11 @@ TIP: Conventionally your inputs will mirror model attributes. However, they don'
#### The `fields_for` Helper
You can create a similar binding without actually creating `<form>` tags with the [`fields_for`](https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-fields_for) helper. This is useful for editing additional model objects with the same form. For example, if you had a `Person` model with an associated `ContactDetail` model, you could create a form for creating both like so:
The [`fields_for`][] helper creates a similar binding but without rendering a
`<form>` tag. This can be used to render fields for additional model objects
within the same form. For example, if you had a `Person` model with an
associated `ContactDetail` model, you could create a single form for both like
so:
```erb
<%= form_with model: @person do |person_form| %>
@ -249,7 +253,7 @@ You can create a similar binding without actually creating `<form>` tags with th
<% end %>
```
which produces the following output:
Which produces the following output:
```html
<form action="/people" accept-charset="UTF-8" method="post">
@ -259,7 +263,10 @@ which produces the following output:
</form>
```
The object yielded by `fields_for` is a form builder like the one yielded by `form_with`.
The object yielded by `fields_for` is a form builder like the one yielded by
`form_with`.
[`fields_for`]: https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-fields_for
### Relying on Record Identification
@ -794,9 +801,10 @@ There's a restriction, however: while hashes can be nested arbitrarily, only one
WARNING: Array parameters do not play well with the `check_box` helper. According to the HTML specification unchecked checkboxes submit no value. However it is often convenient for a checkbox to always submit a value. The `check_box` helper fakes this by creating an auxiliary hidden input with the same name. If the checkbox is unchecked only the hidden input is submitted and if it is checked then both are submitted but the value submitted by the checkbox takes precedence.
### The `fields_for` Helper
### The `fields_for` Helper `:index` Option
Let's say we want to render a form with a set of fields for each of a person's addresses. The `fields_for` helper and its `:index` argument can assist with this:
Let's say we want to render a form with a set of fields for each of a person's
addresses. The [`fields_for`][] helper with its `:index` option can assist:
```erb
<%= form_with model: @person do |person_form| %>
@ -809,7 +817,8 @@ Let's say we want to render a form with a set of fields for each of a person's a
<% end %>
```
Assuming the person had two addresses with ids 23 and 45, this would create output similar to this:
Assuming the person has two addresses with IDs 23 and 45, the above form would
render output similar to:
```html
<form accept-charset="UTF-8" action="/people/1" method="post">
@ -820,22 +829,35 @@ Assuming the person had two addresses with ids 23 and 45, this would create outp
</form>
```
This will result in a `params` hash that looks like
Which will result in a `params` hash that looks like:
```ruby
{'person' => {'name' => 'Bob', 'address' => {'23' => {'city' => 'Paris'}, '45' => {'city' => 'London'}}}}
{
"person" => {
"name" => "Bob",
"address" => {
"23" => {
"city" => "Paris"
},
"45" => {
"city" => "London"
}
}
}
}
```
Rails knows that all these inputs should be part of the person hash because you
called `fields_for` on the first form builder. By specifying an `:index` option
you're telling Rails that instead of naming the inputs `person[address][city]`
it should insert that index surrounded by [] between the address and the city.
This is often useful as it is then easy to locate which Address record
should be modified. You can pass numbers with some other significance,
strings or even `nil` (which will result in an array parameter being created).
All of the form inputs map to the `"person"` hash because we called `fields_for`
on the `person_form` form builder. By specifying an `:index` option, we mapped
the address inputs to `person[address][#{address.id}][city]` instead of
`person[address][city]`. Thus we are able to determine which Address records
should be modified when processing the `params` hash.
To create more intricate nestings, you can specify the first part of the input
name (`person[address]` in the previous example) explicitly:
You can pass other numbers or strings of significance via the `:index` option.
You can even pass `nil`, which will produce an array parameter.
To create more intricate nestings, you can specify the leading portion of the
input name explicitly. For example:
```erb
<%= fields_for 'person[address][primary]', address, index: address.id do |address_form| %>
@ -843,15 +865,22 @@ name (`person[address]` in the previous example) explicitly:
<% end %>
```
will create inputs like
will create inputs like:
```html
<input id="person_address_primary_1_city" name="person[address][primary][1][city]" type="text" value="Bologna" />
<input id="person_address_primary_23_city" name="person[address][primary][23][city]" type="text" value="Paris" />
```
As a general rule the final input name is the concatenation of the name given to `fields_for`/`form_with`, the index value, and the name of the attribute. You can also pass an `:index` option directly to helpers such as `text_field`, but it is usually less repetitive to specify this at the form builder level rather than on individual input controls.
You can also pass an `:index` option directly to helpers such as `text_field`,
but it is usually less repetitive to specify this at the form builder level
than on individual input fields.
As a shortcut you can append [] to the name and omit the `:index` option. This is the same as specifying `index: address.id` so
Speaking generally, the final input name will be a concatenation of the name
given to `fields_for` / `form_with`, the `:index` option value, and the name of
the attribute.
Lastly, as a shortcut, instead of specifying an ID for `:index` (e.g.
`index: address.id`), you can append `"[]"` to the given name. For example:
```erb
<%= fields_for 'person[address][primary][]', address do |address_form| %>
@ -859,7 +888,7 @@ As a shortcut you can append [] to the name and omit the `:index` option. This i
<% end %>
```
produces exactly the same output as the previous example.
produces exactly the same output as our original example.
Forms to External Resources
---------------------------