Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails validation error messages: Displaying only one error message per field

Rails displays all validation error messages associated with a given field. If I have three validates_XXXXX_of :email, and I leave the field blank, I get three messages in the error list.

Example:

validates_presence_of :name validates_presence_of :email validates_presence_of :text  validates_length_of :name, :in => 6..30 validates_length_of :email, :in => 4..40 validates_length_of :text, :in => 4..200  validates_format_of :email, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i<br/> 

<%= error_messages_for :comment %> gives me:

7 errors prohibited this comment from being saved  There were problems with the following fields:  Name can't be blank Name is too short (minimum is 6 characters) Email can't be blank Email is too short (minimum is 4 characters) Email is invalid Text can't be blank Text is too short (minimum is 4 characters) 

It is better to display one messages at a time. Is there an easy way to fix this problem? It looks straightforward to have a condition like: If you found an error for :email, stop validating :email and skip to the other field.

like image 831
TraderJoeChicago Avatar asked Apr 02 '10 21:04

TraderJoeChicago


1 Answers

[Update] Jan/2013 to Rails 3.2.x - update syntax; add spec

Inspired by new validation methods in Rails 3.0 I'm adding this tiny Validator. I call it ReduceValidator.

lib/reduce_validator.rb:

# show only one error message per field # class ReduceValidator < ActiveModel::EachValidator    def validate_each(record, attribute, value)     return until record.errors.messages.has_key?(attribute)     record.errors[attribute].slice!(-1) until record.errors[attribute].size <= 1   end end 

My Model looking like - notice the :reduce => true:

validates :title, :presence => true, :inclusion => { :in => %w[ Mr Mrs ] }, :reduce => true validates :firstname, :presence => true, :length => { :within => 2..50 }, :format => { :without => /^\D{1}[.]/i }, :reduce => true validates :lastname, :presence => true, :length => { :within => 2..50 }, :format => { :without => /^\D{1}[.]/i }, :reduce => true 

Works like a charm in my current Rails Project. The advantageous is, i've put the validator only on a few fields not all.

spec/lib/reduce_validator_spec.rb:

require 'spec_helper'  describe ReduceValidator do    let(:reduce_validator) { ReduceValidator.new({ :attributes => {} }) }    let(:item) { mock_model("Item") }   subject { item }    before(:each) do     item.errors.add(:name, "message one")     item.errors.add(:name, "message two")   end    it { should have(2).error_on(:name) }    it "should reduce error messages" do     reduce_validator.validate_each(item, :name, '')     should have(1).error_on(:name)   end  end 
like image 159
rzar Avatar answered Sep 23 '22 10:09

rzar