Pretty Error Messages in Rails Saturday morning, 24 June 2006

One of the cool things about Rails is it’s easy to use, easily extensible form validation methods. You have a model file which represents a table in a database or a shopping cart and adding validations to the attributes is just a case of writing a few lines of code. E.g.

class Article < ActiveRecord::Base
  belongs_to :issue
  acts_as_list :scope => :issue
  validates_uniqueness_of :title, :linkname
  validates_presence_of :title, :linkname
end

Those last two lines of the class definition make sure that the title and linkname attributes are filled in and unique when the user submits the form (or if it's saved by another means).

Rails also has a method of outputting the error messages from the validation, and it’s another one-liner: <%= error_messages_for 'article' %>. However, the output of these error messages is sometimes not as human as you would like. Even though you can customise the error message returned with the :message parameter, you still get the name of the attribute in front of it, and it looks ugly!

So, I went about solving this issue to get prettier error messages with the following three requirements:

  1. Must be simple to use and not require mucking around with the Rails framework.
  2. Must allow complete customisation of the error output.
  3. Must be optional, i.e. you can choose to use it or not for a given form.

The answer for me was to make a copy of the Rails error_messages_for and duplicate it in a helper file. As I wanted to use it in many places I added my new method the file /helpers/application_helper.rb.

def pretty_error_messages_for(object_name, options = {})
         options = options.symbolize_keys
           object = instance_variable_get("@#{object_name}")
           if object && !object.errors.empty?
             content_tag("div",
               content_tag(
                 options[:header_tag] || "h4",
                 "#{pluralize(object.errors.count, "error")} prohibited this #{object_name.to_s.gsub("_", " ")} from being saved:"
               ) +
               content_tag("ul", object.errors.collect { |msg| content_tag("li", msg[1]) }),
               "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
             )
           else
           ""
       end
   end

This satisfies my first requirement, it’s just one method to drop into the application helper file. It also satisfies my second. In this example I haven’t changed very much. The header is reduced to a heading 4 and i've changed the output to only return the error message, not the name of the attribute.

To use it you just replace error_message_for with pretty_error_messages_for in your form partial, or wherever you want to output the error messages. If you don’t want to use then just don’t change that line of code.

The only disadvantage with this particular error_messages_for variant, is that if you use it you are obliged to list your validations separately and provide full error messages for each of them, thus:

class Article < ActiveRecord::Base
  belongs_to :issue
  acts_as_list :scope => :issue
  validates_uniqueness_of :title, :message => "The Title must be unique"
  validates_uniqueness_of :linkname, :message => "The Link Name must be unique"
  validates_presence_of :title, :message => "The Title can&#8217;t be blank"
  validates_presence_of :linkname, :message => "The Link Name can&#8217;t be blank"
end

However, it gives you complete control over the output of the error messages, which for the very small amount of added work I feel is well worth the benefit to users.

 

Comments

 

Get Around

Journal
  • contemplating.Thoughts from a Christian world-view.
  • enjoying.Reviews of stuff i've been enjoying.
  • life.For those that would like to know what i'm up to, this is the place to look.
  • working.Thoughts and ideas on web development and projects i'm working on.
Other Places