Validations with form_tag
Validations with form_tag
form_tag
Now that we've learned to handle the server side of validations, we need to take care of the client side.
At this point, we'll be in step three of the following flow:
User fills out the form and hits "Submit", transmitting the form data via a POST request.
The controller sees that validations have failed, and re-renders the form.
The view displays the errors to the user.
Objectives
After this lesson, you'll be able to...
Prefill in form values based on an instance
Print out full error messages based on an invalid instance
Introspect on errors for a field
Apply an error class to invalid fields
Pre-Filling Form Values
No one likes re-doing work. First, let's make sure we know how to pre-fill forms with the user's input so they don't have to type everything all over again.
There are two ways to pre-fill forms in Rails: form_tag
and form_for
. form_for
is very heavy on Rails magic and continues to baffle scientists to this day, so we'll be going over form_tag
first.
Here's what the HTML output will look like:
We're working with this Person
model:
This means validation will fail if we put numbers into the "Name" field, and the form will be re-rendered with the invalid @person
object available.
Remember that our create
action now looks like this:
With this in mind, we can use the invalid @person
object to "re-fill" the usually-empty new
form with the user's invalid entries. This way they don't have to re-type anything.
(You wouldn't always want to do this –– for example, with credit card numbers –– because you want to minimize the amount of times sensitive information travels back and forth over the internet.)
Now, let's plug the information back into the form:
But now it will look like this:
When the browser renders those inputs, they'll be pre-filled with the data in their value
attributes.
This is the same technique used to create edit
/update
forms.
We can also use the same form code for empty and pre-filled forms because @person = Person.new
will create an empty model object whose attributes are all nil
.
Displaying All Errors With errors.full_messages
errors.full_messages
The simplest way to show errors is to just spit them all out at the top of the form by iterating over @person.errors.full_messages
. But first, we'll have to check whether there are errors to display with @person.errors.any?
.
If the model has two errors, there will be two items in full_messages
, which could result in the following HTML:
This is nice, but it's not very helpful from a user interface standpoint. It would be much better if the incorrect fields themselves were highlighted somehow.
Displaying Pre-Field Errors With errors[]
errors[]
ActiveModel::Errors
has much more than just a list of full_message
error strings. It can also be used to access field-specific errors by interacting with it like a hash. If the field has errors, they will be returned in an array of strings:
With this in mind, we can conditionally "error-ify" each field in the form, targeting the divs containing each field:
Rails will add a class if there are errors, but you can manually do so like this:
You can override ActionView::Base.field_error_proc
to change it to something that suits your UI. It's currently defined as this within ActionView::Base:
.
Note: There is a deliberate space added in ' field_with_errors'
in the example above. If @person.errors[:name].any?
validates to true, the goal here is to produce two class names separated by a space (class=field field_with_errors
). Without the added space, we would get class=fieldfield_with_errors
instead!
The Whole Picture
By now, our full form has grown quite a bit:
Notice that some whitespace has been added for "breathing room" and increased readability. Additionally, indentation has been very carefully maintained.
It's already starting to feel pretty unwieldy to manually manage all of this conditional display logic, but, without an understanding of the dirty details, we can't even begin to use more powerful tools like form_for
correctly.
Next, we'll dive into a lab using form_tag
and artisanally craft our own markup.
Last updated