Rails显示与给定字段关联的所有验证错误消息。如果我有三个validates_XXXXX_of :email
,并且我将该字段留空,我会在错误列表中收到三条消息。
示例:
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 %>
给了我:
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)
最好一次显示一条消息。有没有一种简单的方法来解决这个问题?看起来很简单:如果您发现:email
错误,请停止验证:email
并跳到其他字段。
答案 0 :(得分:34)
[更新] 2013年1月到Rails 3.2.x - 更新语法;添加规范
受Rails 3.0中新的validation方法的启发,我正在添加这个微小的Validator。我称之为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
我的模型看起来像 - 注意 :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
在我目前的Rails项目中扮演一个魅力。 有利的是,我把验证器只放在几个领域而不是全部。
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
答案 1 :(得分:16)
Imo简化是:
<% @model.errors.each do |attr, msg| %>
<%= "#{attr} #{msg}" if @model.errors[attr].first == msg %>
<% end %>
答案 2 :(得分:10)
伯特在RailsForum过了一会儿写了这篇文章。他编写了下面的代码,并添加了一些小调整,以便在Rails-3.0.0-beta2上运行。
将此添加到名为app/helpers/errors_helper.rb
的文件中,只需将helper "errors"
添加到您的控制器即可。
module ErrorsHelper
# see: lib/action_view/helpers/active_model_helper.rb
def error_messages_for(*params)
options = params.extract_options!.symbolize_keys
objects = Array.wrap(options.delete(:object) || params).map do |object|
object = instance_variable_get("@#{object}") unless object.respond_to?(:to_model)
object = convert_to_model(object)
if object.class.respond_to?(:model_name)
options[:object_name] ||= object.class.model_name.human.downcase
end
object
end
objects.compact!
count = objects.inject(0) {|sum, object| sum + object.errors.count }
unless count.zero?
html = {}
[:id, :class].each do |key|
if options.include?(key)
value = options[key]
html[key] = value unless value.blank?
else
html[key] = 'errorExplanation'
end
end
options[:object_name] ||= params.first
I18n.with_options :locale => options[:locale], :scope => [:errors, :template] do |locale|
header_message = if options.include?(:header_message)
options[:header_message]
else
locale.t :header, :count => count, :model => options[:object_name].to_s.gsub('_', ' ')
end
message = options.include?(:message) ? options[:message] : locale.t(:body)
error_messages = objects.sum do |object|
object.errors.on(:name)
full_flat_messages(object).map do |msg|
content_tag(:li, ERB::Util.html_escape(msg))
end
end.join.html_safe
contents = ''
contents << content_tag(options[:header_tag] || :h2, header_message) unless header_message.blank?
contents << content_tag(:p, message) unless message.blank?
contents << content_tag(:ul, error_messages)
content_tag(:div, contents.html_safe, html)
end
else
''
end
end
####################
#
# added to make the errors display in a single line per field
#
####################
def full_flat_messages(object)
full_messages = []
object.errors.each_key do |attr|
msg_part=msg=''
object.errors[attr].each do |message|
next unless message
if attr == "base"
full_messages << message
else
msg=object.class.human_attribute_name(attr)
msg_part+= I18n.t('activerecord.errors.format.separator', :default => ' ') + (msg_part=="" ? '': ' & ' ) + message
end
end
full_messages << "#{msg} #{msg_part}" if msg!=""
end
full_messages
end
end
答案 3 :(得分:3)
这个怎么样?
@event.errors[:title].first
?
答案 4 :(得分:3)
我写了一个自定义助手
def display_error(field)
if @user.errors[field].any?
raw @user.errors[field].first+"<br>"
end
end
然后我在文本字段中的视图中使用它,如此
<%= display_error(:password) %>
答案 5 :(得分:2)
我将此代码用于Ruby on Rails 3.0版本,我将其放入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
这与ActiveModel::Errors#full_messages
基本相同,但每个属性不会显示多个错误。请务必要求该文件(例如,在初始化程序中),现在您可以调用@model.errors.full_message_per_field do |message| ...
答案 6 :(得分:2)
与olovwia的答案类似:
<% @errors.keys.each do |attr| %>
<%= "#{attr.capitalize} #{@errors[attr].first}."%>
<% end %>"
答案 7 :(得分:1)
向ActiveModel :: Errors类添加方法
module ActiveModel
class Errors
def full_unique_messages
unique_messages = messages.map { |attribute, list_of_messages| [attribute, list_of_messages.first] }
unique_messages.map { |attribute_message_pair| full_message *attribute_message_pair }
end
end
end
将其添加到文件中,例如lib/core_ext/rails/active_model/errors.rb
。创建文件config/initializers/core_ext.rb
并向其添加require "core_ext/rails/active_model/errors.rb"
。
答案 8 :(得分:1)
我会在一行和句子格式中显示所有错误消息。您不希望用户修复一个错误,并最终在提交后发现另一个他不知道的错误。告诉他们所有规则将为他们节省点击次数。话虽如此,我就是这样做的:
flash_message_now("error",
@album.errors.keys.map { |k| "#{Album.human_attribute_name(k)} #{@album.errors[k].to_sentence}"}.to_sentence
)
在ApplicationController中定义了flash_message_now(您可以将其添加到帮助程序中)
def flash_message_now(type, text)
flash.now[type] ||= []
flash.now[type] << text
end
答案 9 :(得分:0)
或者你可以简单地修改数组(使用'bang'方法delete_at),所以在保留默认rails,i18n等后的所有内容。
<% @article.errors.keys.each { |attr| @article.errors[attr].delete_at(1) } %>
完整的工作代码:
<% if @article.errors.any? %>
<% @article.errors.keys.each { |attr| @article.errors[attr].delete_at(1) } %>
<ul>
<% @article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
答案 10 :(得分:0)
# Extracts at most <strong>one error</strong> message <strong>per field</strong> from the errors-object.
# @param [ActiveModel::Errors] the_errors_object The errors-object.
# @raise [ArgumentError] If the given argument is not an instance of ActiveModel::Errors.
# @return [Array] A string-array containing at most one error message per field from the given errors-object.
def get_one_error_per_field(the_errors_object)
if the_errors_object.is_a? ActiveModel::Errors
errors = {}
the_errors_object.each do |field_name, associated_error|
errors[field_name] = the_errors_object.full_message(field_name, associated_error) unless errors[field_name]
end
return errors.values
else
raise ArgumentError.new('The given argument isn\'t an instance of ActiveModel::Errors!')
end
end
答案 11 :(得分:0)
我的ActiveModel::Errors
类lib/core_ext/rails/active_model/errors.rb
的猴子补丁(我在Ruby on Rails 5.0版本中使用此代码):
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
创建文件config/initializers/core_ext.rb
并向其添加需求core_ext/rails/active_model/errors.rb
。
答案 12 :(得分:-2)
我认为最简单的方法是使用allow_bank选项。 例如,要避免在字段留空时显示名称太短的消息,您可以执行以下操作:
validates_length_of :name, allow_blank:true, :in => 6..30