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
I would display all the error messages on one line and in a sentence format. You don't want the user to fix one error and end up having another error he was not aware of after submission. Telling them all the rules will save them clicks. With that said, this is how I'd do it:
flash_message_now("error",
@album.errors.keys.map { |k| "#{Album.human_attribute_name(k)} #{@album.errors[k].to_sentence}"}.to_sentence
)
with flash_message_now defined in ApplicationController (you can add it to a helper)
def flash_message_now(type, text)
flash.now[type] ||= []
flash.now[type] << text
end
[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
How about this
@event.errors[:title].first
?
Similar to olovwia's answer:
<% @errors.keys.each do |attr| %>
<%= "#{attr.capitalize} #{@errors[attr].first}."%>
<% end %>"
My monkey patch of ActiveModel::Errors
class lib/core_ext/rails/active_model/errors.rb
(I use this code for Ruby on Rails 5.0 release):
module ActiveModel
class Errors
# don't add an attribute's error message to details
# if it already contains at least one message
alias_method :old_add, :add
def add(attribute, message = :invalid, options = {})
if details[attribute.to_sym].size.zero?
old_add(attribute, message, options)
end
end
end
end
Create a file config/initializers/core_ext.rb
and add a require core_ext/rails/active_model/errors.rb
to it.
I use this code for Ruby on Rails 3.0 release, which I put in lib/core_ext/rails/active_model/errors.rb
:
module ActiveModel
class Errors
def full_message_per_field
messages_per_field = []
handled_attributes = []
each do |attribute, messages|
next if handled_attributes.include? attribute
messages = Array.wrap(messages)
next if messages.empty?
if attribute == :base
messages_per_field << messages.first
else
attr_name = attribute.to_s.gsub('.', '_').humanize
attr_name = @base.class.human_attribute_name(attribute, :default => attr_name)
options = { :default => "%{attribute} %{message}", :attribute => attr_name }
messages_per_field << I18n.t(:"errors.format", options.merge(:message => messages.first))
end
handled_attributes << attribute
end
messages_per_field
end
end
end
This is essentially the same code as ActiveModel::Errors#full_messages
, but won't show more than one error per attribute. Be sure to require the file (say, in an initializer) and now you can call @model.errors.full_message_per_field do |message| ...