Customize the Error Reporting for ActiveRecord Forms
When a form in your Rails application is used to edit (or create) an ActiveRecord instance, ActionView uses the ActiveRecordHelper module to create the HTML input field tags. In the event of an error, your input field is wrapped inside a DIV with class=”fieldWithErrors”. This isn’t so bad, but you’re left with few options for customizing the look of your forms when there is an error. For example, what if you wanted to put the error text associated with a field right next to the field?
One way to do this is by putting additional markup in your forms. Something like:
<% if object.errors.on(:some_field) %> <span class="error"><%= object.errors.on(:some_field) %></span> <% end %> ... rest of field markup ... |
That works, but you have to repeat the fields at least twice: once for the potential error condition, and once for the field itself.
You could write up a new form helper that includes the boilerplate error markup, but that falls apart as soon as you want to put in a set of checkbox or radio button controls. Maybe you only want one error message for the entire group, which is almost certainly true for radio buttons.
Another alternative that I don’t see widely acknowledged is to override the standard Proc used by the ActiveRecordHelper for errors: ActionView::Base.field_errro_proc. Here’s an example:
ActionView::Base.field_error_proc = Proc.new do |html_tag, instance| error_text = "" errors = instance.object.errors.on(instance.method_name) if errors errors.to_a.each do |error| error_text << "<p class=\"error\">#{error}</p>" end end "<div class=\"errors\">#{error_text}</div>#{html_tag}" end |
The Proc instance is passed the HTML tag (already built into a string) and an instance of InstanceTag. The latter is the class used by many of the ActionView helpers for building HTML tags that correspond to a particular piece of data in an object. InstanceTag keeps a reference back to that object and method, and you can make use of it in your custom field_error_proc, as shown above.
One little annoyance you don’t see until you start down this path is that the method that builds checkboxes always includes a hidden field with the same name. This is done to ensure that some value for that field is always sent to the server when the form is submitted. The trouble is that the hidden field gets no special treatment from ActiveRecordHelper: field_error_proc is called twice, once for the checkbox field and once for the hidden field, and custom error messaging, as shown above, gets rendered twice. This can be fixed by overriding InstanceTag#tag:
def tag(name, options) if object.respond_to?(:errors) && object.errors.respond_to?(:on) suppress_errors = (name == "input" && options["type"] == "hidden") error_wrapping(tag_without_error_wrapping(name, options), object.errors.on(@method_name) && !suppress_errors) else tag_without_error_wrapping(name, options) end end |