Forms with form_with
Forms are where members hand data to Bookclub, and they have to agree with three other parts of the app: the route (which URL and method), the controller (which params), and the model (which errors). form_with builds forms that agree with all three automatically.
The rule
Give form_with the model; it picks the URL, the method and the names.
<%# app/views/books/_form.html.erb %>
<%= form_with model: book do |f| %>
<% if book.errors.any? %>
<div class="errors">
<h2><%= pluralize(book.errors.count, "error") %> stopped this book being saved:</h2>
<ul>
<% book.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.label :title %>
<%= f.text_field :title %>
<%= f.label :pages %>
<%= f.number_field :pages %>
<%= f.label :genre %>
<%= f.select :genre, ["Fantasy", "History", "Science"] %>
<%= f.check_box :available %>
<%= f.label :available %>
<%= f.submit %>
<% end %>What form_with model: book decides for you:
| Book is… | Form goes to | Method | Button says |
|---|---|---|---|
| new (not saved) | /books |
POST → create |
"Create Book" |
| saved (id 7) | /books/7 |
PATCH → update |
"Update Book" |
Browsers only send GET and POST, so for PATCH Rails adds a hidden _method field. It also adds a hidden authenticity token that proves the form came from your site (CSRF protection).
Field helpers name inputs book[title], book[pages] and so on, so everything arrives under params[:book] — exactly what book_params permits. Each helper also fills in the current value, so the edit form shows the saved data.
For nested resources pass both records: form_with model: [@book, @review] posts to /books/7/reviews. Without a model, give a URL: form_with url: search_path, method: :get.
You will meet form_for in older code. It did the same job and is replaced by form_with.
The shortcut
Trace the loop: form → params → model → errors → form. If the save fails, the controller renders the form with the same object, so the typed values and the error list come back together.
Submit
The new form posts
book[title]=""andbook[pages]=412to/books.
The trap is redirecting after a failed save. The new request has a fresh, empty book, so the errors and the typed values are lost.
Drills
Answer fast — trust the rule.
form_with model: Book.new submits to which URL and method?
What name does f.text_field :title give its input inside form_with model: @book?
Why does a Rails form include a hidden authenticity token?
Which call builds a review form for @book that posts to /books/7/reviews?
A save fails and the controller calls redirect_to new_book_path. What does the member see?
Remember
- Give form_with the model; it picks the URL, the method and the names.
- Shortcut: form → params → model → errors → form, via render on failure.
- Trap: redirecting after a failed save loses the errors.
