We build Web & Mobile Applications.
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.