Challenge 3: Displaying Errors in a Form — Solution
<% if product.errors.any? %>
<% product.errors.full_messages.each do |message| %>
- <%= message %>
<% end %>
<% end %>
<%= form_with model: product do |f| %>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
Step-by-step trace of an invalid submission:
1. (Chapter 7) The Product model has validates :name, presence: true (or
similar) declared directly on the model.
2. (Chapter 3) A user submits the form with a blank name. The create
action runs: @product = Product.new(product_params); if @product.save
... else render :new end. Because validation fails, .save returns
false, so the else branch runs: render :new.
3. (Chapter 7) The failed .save call populated @product.errors
automatically -- @product itself is still in memory with the user's
submitted (invalid) values, exactly as it was built from product_params
moments earlier.
4. (Chapter 3 + this chapter) render :new re-renders the SAME @product
instance -- no new HTTP request is made, no database re-query happens
-- straight into app/views/products/new.html.erb, which itself renders
this chapter's _form.html.erb partial.
5. (This chapter) product.errors.any? is now true, so the error block at
the top of the partial displays every message from
product.errors.full_messages, and the form fields below still show the
user's originally submitted (invalid) values, ready to be corrected and
resubmitted.
=begin
Notes:
- Nothing in this flow involves a second HTTP request -- render :new
(as opposed to redirect_to) is specifically what keeps @product's
in-memory state, including both its errors and the user's typed input,
intact through to the re-rendered form.
- This end-to-end trace is really the payoff of five separate chapters
(validations, controllers, views, and this chapter's forms) all working
together on one single, ordinary "submit an invalid form" interaction.
=end