We build Web & Mobile Applications.

< All Articles

Error fields with a Hpricot twist

The default Rails behaviour for highlighting form fields with errors is to wrap them in a div, so:

<div>
  <%= f.label(:email) %>
  <%= f.text_field(:email, :size => '30') %>
</div>

Becomes:

<div>
  <div class="fieldWithErrors"><label for="user_email">Email</label></div>
  <div class="fieldWithErrors"><input id="user_email" name="user[email]" size="30" type="text" value="" /></div>
</div>

This is great when knocking together a quick prototype but as your site evolves you’ll probably want to customise it. This can be done very easily by using ActionView::Base.field_error_proc, assign a Proc to it either in your environment.rb or, if you’re a modern thinker, in an initializer and you’re good to go.

The Rails wiki has some examples of custom highlighting methods, one of which involves using regular expressions to add a style to error fields, like this:

ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
  msg = instance.error_message
  error_style = "background-color: #f2afaf"
  if html_tag =~ /<(input|textarea|select)[^>]+style=/
    style_attribute = html_tag =~ /style=['"]/
    html_tag.insert(style_attribute + 7, "#{error_style}; ")
  elsif html_tag =~ /<(input|textarea|select)/
    first_whitespace = html_tag =~ /\s/
    html_tag[first_whitespace] = " style='#{error_style}' "
  end
  html_tag
end

There are a few problems with this approach:

For a project I’m working on I wanted to add a CSS class of error to my form fields (including labels) and wanted to do it in a nicer way. Here’s what I came up with:

ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
  if html_tag =~ /<(input|label|textarea|select)/
    error_class = 'error'
    nodes = Hpricot(html_tag)
    nodes.each_child { |node| node[:class] = node.classes.push(error_class).join(' ') unless !node.elem? || node[:type] == 'hidden' || node.classes.include?(error_class) }
    nodes.to_html
  else
    html_tag
  end
end

And here’s an example of the output:

<div>
  <label class="error" for="user_email">Email</label>
  <input name="user[email]" size="30" class="error" type="text" id="user_email" value="" />
</div>

To add the class I’ve used why’s super-speedy Hpricot HTML parser and, to me at least, this looks far nicer than regular expressions and string inserts (although one regular expression remains to skip the tag if it doesn’t need any further processing). It even checks to make sure the error class isn’t added twice.

Using an HTML parser may seem like overkill but a quick benchmark shows it takes less than 0.01 of a second to do its thing: for me the cleaner looking code and the ability to easily manipulate the HTML element using Hpricot makes this a worthwhile approach.

Updated on 07 February 2019
First published by Rob Anderton on 21 April 2008
© Rob Anderton 2019
"Error fields with a Hpricot twist" by Rob Anderton at TheWebFellas is licensed under a Creative Commons Attribution 4.0 International License.