Easy Peasy Form Validation Errors with Rails Turbo Frames (modals)
Form Validations (or officially Active Record Validations) are a fundamental feature of the Rails framework since its very beginning.
Throughout this post I will continue using Form Validations, as I not only use them with Active Record objects (eg. Model), but (more often) with Form Objects (eg. SignupForm).
They ensure that only valid data is saved to the database, enforcing rules and constraints on model attributes to maintain data integrity and, also important, improve user experience.
I use Form Validations quite a lot, as it’s a super easy way to provide my app’s users with feedback about what’s incorrect about the data they submitted.
As a little side-note: I mostly use Form Validations for UX reasons when submitting forms, the data integrity is better handled on a database level.
Quick Primer on Form Validations
Rails comes with common built-in validators, such as presence, uniqueness, format, length and many others.
class Comment < ApplicationRecord
belongs_to :post
validates :body, presence: true, length: { minimum: 10 }
end
When you try to save a record, like this Comment without a body, Rails returns an ActiveModel::Errors object to hold errors related to validation failures. In your view you can display them like so:
<% if @comment.errors.any? %>
<% @comment.errors.full_messages.each do |message| %>
<%= message %>
<% end %>
<% end %>
But when you have a form in a modal or slide-over (that both use turbo-frames), you notice that suddenly your app breaks when validation errors appear.
How to Show Form Validations in a Turbo Frame
Let’s see how to get this working with turbo-frames.
The comments form is shown in a modal like below. It’s displayed after clicking a link like this link_to "Comment", new_comment_path, data: {turbo_frame: "modal"}.
# app/views/comments/new.html.erb
<%= ModalComponent.new %>
<%= component "comments/form", comment: @comment %>
<% end %>
(this is using the Rails Designer ModalComponent)
The controller is as vanilla-Rails as it gets. Just take note of status: :unprocessable_entity part. This sends a 422 status code to the browser that Turbo requires/expects to be able to replace the content with the response.
# app/controllers/comments_controller.rb
def create
@comment = Comment.new(comment_params)
respond_to do |format|
if @comment.save
flash.now[:success] = Created
format.turbo_stream
format.html { redirect_to comments_path, notice: "Created" }
else
flash.now[:success] = "Error"
format.turbo_stream { render :new, status: :unprocessable_entity }
format.html { render :new, status: :unprocessable_entity }
end
end
end
Here the block, for both the html-format and the turbo_stream-format, are the same. For HTML responses it uses the new.html.erb view template. So now create a app/views/comments/new.turbo_stream.html turbo-stream template and you are off to the races.
# app/views/comments/new.turbo_stream.erb
<%= turbo_stream.replace "modal" do %>
<%= ModalComponent.new %>
<%= component "comments/form", comment: @comment %>
<% end %>
<% end %>
Notice that the content is almost the same as the app/views/comments/new.html.erb view template. I’m fine with this little duplication.
And that is all that is needed to display Form Validations in your turbo-frame Modals and Slide-overs.
Want to read me more?
-
How to implement and customize Rails Flash Messages (notifications)
Explore Rails flash messages, from simple notifications to advanced user interactions with Turbo Streams. Learn best practices for temporary messages, error notices and real-time alerts in Rails applications. Perfect for developers seeking to enhance user experience with timely feedback. -
How to create Modals with Rails and Hotwire (and Tailwind CSS)
Creating modals in Rails with Hotwire (and Tailwind CSS) is a straightforward process. Let's go through the process step by step. -
Turbo Drive, Frames, Streams, Morph? What to use?!
Hotwire comes with various Turbo options. It is not always clear which one to use and when. This article explains my thoughts and a guideline to help you choose the right tool for the job.
Over to you…
What did you like about this article? Learned something knew? Found something is missing or even broken? 🫣 Let me (and others) know!
Comments are powered by Chirp Form
{{comment}}