November 5, 2006
Live Forms. Part 1: Putting the errors in the right place
This is Part 1 of a series describing how to make better forms for Rails.
The standard rails approach to handling form validation errors is to spit out a long list of all validation errors in a box at the top of the form. The problem with this approach is that there is a separation between the list of errors and the field that spawned them, like this.

This can be a bit annoying, particularly in a long form where the error message and the offending form field may not appear on the screen at the same time. Notice that you have to first read the error message, and then read the titles of all the fields to find the offending one(s) that need to be corrected. They aren’t even in the same order as they were specified in the form, which makes it even more confusing. This is just extra work for the user, particularly if your form is long or complex.
One way to address this problem is to put the appropriate error message right next to the field with the error. This way, it’s pretty obvious where the problems are, and even what they are. You can also generally avoid using the name of the field in the message.
A simple example of this might look like this….

This still isn’t pretty, but at least the user knows exactly which fields need correcting, and a little CSS magic can even place the message to the right of the offending field.
So how do we implement this?
Simple… first go into your view and delete the
line.
Then stick this in your application helper.
-
# application_helper.rb
-
def error_for(object, method = nil, options={})
-
if method
-
err = instance_variable_get("@#{object}").errors.on(method).to_sentence rescue instance_variable_get("@#{object}").errors.on(method)
-
else
-
err = @errors["#{object}"] rescue nil
-
end
-
options.merge!(:class=>’fieldWithErrors’, :id=>"#{[object,method].compact.join(’_')}-error", :style=>(err ? "#{options[:style]}" : "#{options[:style]};display: none;"))
-
content_tag("p",err || "", options )
-
end
Then in your form view, add an ‘error_for’ call wherever you need one…
-
# _form.rhtml
-
<p><label for="code_project_name">Name</label>
-
<%= text_field ‘code_project’, ‘name’ %>
-
<%= error_for ‘code_project’, ‘name’ %></p>
If the model fails a validation test, then it will show the message right next to the field that caused the validation problem.
Also note that if you define an instance variable called @errors containing a hash of field_names and messages, they will also be used. This is handy for those form fields that don’t correspond to a model attribute.
Stay tuned for Part 2, where I will describe a how to make these error message dynamic with AJAX.
Filed by Kevin Olbrich at 10:33 pm under Forms, Ruby on Rails, User Interface
8 Comments
Looks useful. Could you add a bit of explnation for the application helper?
I’ve an error with it : “err = instance_variable_get(”@#{object}”).errors.on(method).to_sentence rescue instance_variable_get(”@#{object}”).errors.on(method)” make crashed :
You have a nil object when you didn’t expect it!
You might have expected an instance of ActiveRecord::Base.
The error occured while evaluating nil.errors
You can do it the short way:
will display any error asociated with a validation with the given attribute
e.g
@person.name
I also got the same error message as Jean-Sebastien. It’s a great article, when will the 2nd part of this series be posted?
Hopefully part 2 will be coming along soon, my schedule should be clearing up in a week or so.
The problem stems from the ‘errors’ array not being initialized. If you create a new object from scratch, it generally isn’t.
My suggestion is to call ‘valid?’ on your object in the controller action before the view is rendered.
Working! Thanks for the reply Kevin. Looking forward to part 2.
[...] Il est intéressant de pouvoir séparer les erreurs les unes des autres, cela permet ainsi d’afficher les erreurs concernant le login près du login et les erreurs concernant le mot de passe près du mot de passe. J’utilise pour cela la méthode trouvée ici. [...]
What if I want to include the field name at the beginning of the error message?