Ruby-on-rails Rails 验证错误消息:每个字段仅显示一条错误消息
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2569401/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Rails validation error messages: Displaying only one error message per field
提问by TraderJoeChicago
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.
Rails 显示与给定字段关联的所有验证错误消息。如果我有三个validates_XXXXX_of :email,并且我将该字段留空,我会在错误列表中收到三条消息。
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:
<%= 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)
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 :emailand skip to the other field.
最好一次显示一条消息。有没有简单的方法来解决这个问题?具有以下条件看起来很简单:如果您发现 错误:email,请停止验证:email并跳到另一个字段。
采纳答案by Nate Murray
Bert over at RailsForumwrote about this a little while back. He wrote the code below and I added some minor tweaks for it to run on Rails-3.0.0-beta2.
不久前,RailsForum 的Bert写了一篇关于这个的文章。他写了下面的代码,我添加了一些小的调整让它在 Rails-3.0.0-beta2 上运行。
Add this to a file called app/helpers/errors_helper.rband simply add helper "errors"to your controller.
将此添加到一个名为的文件中,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
回答by rzar
[Update]Jan/2013 to Rails 3.2.x - update syntax; add spec
[更新]2013 年 1 月到 Rails 3.2.x - 更新语法;添加规格
Inspired by new validationmethods in Rails 3.0 I'm adding this tiny Validator. I call it ReduceValidator.
受Rails 3.0 中新验证方法的启发,我添加了这个微型验证器。我称之为ReduceValidator。
lib/reduce_validator.rb:
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:
我的模型看起来像 - 注意: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.
在我当前的 Rails 项目中就像一个魅力。优点是,我只将验证器放在了几个字段上,而不是全部。
spec/lib/reduce_validator_spec.rb:
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
回答by olownia
Imo simplier is:
Imo 更简单的是:
<% @model.errors.each do |attr, msg| %>
<%= "#{attr} #{msg}" if @model.errors[attr].first == msg %>
<% end %>
回答by jawaica
How about this
@event.errors[:title].first?
这个@event.errors[:title].first怎么样
?
回答by Maciej Goszczycki
I wrote a custom helper
我写了一个自定义助手
def display_error(field)
if @user.errors[field].any?
raw @user.errors[field].first+"<br>"
end
end
and then I use it in view under the text field like so
然后我像这样在文本字段下的视图中使用它
<%= display_error(:password) %>
回答by Michelle Tilley
I use this code for Ruby on Rails 3.0 release, which I put in lib/core_ext/rails/active_model/errors.rb:
我将此代码用于 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
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| ...
这与 本质上是相同的代码ActiveModel::Errors#full_messages,但不会为每个属性显示一个以上的错误。确保需要该文件(例如,在初始化程序中),现在您可以调用@model.errors.full_message_per_field do |message| ...
回答by Alexander
回答by Frank B
Or you can simply modify the array (with 'bang' method delete_at), so everything after stays default rails, i18n etc.
或者您可以简单地修改数组(使用'bang'方法delete_at),因此之后的所有内容都保持默认rails,i18n等。
<% @article.errors.keys.each { |attr| @article.errors[attr].delete_at(1) } %>
Complete working code:
完整的工作代码:
<% 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 %>
回答by Coffee Bite
Add a method to ActiveModel::Errors class
向 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
Add it to a file, like lib/core_ext/rails/active_model/errors.rb. Create a file config/initializers/core_ext.rband add a require "core_ext/rails/active_model/errors.rb"to it.
将其添加到文件中,例如lib/core_ext/rails/active_model/errors.rb. 创建一个文件config/initializers/core_ext.rb并添加一个require "core_ext/rails/active_model/errors.rb"。
回答by Abdo
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)
在 ApplicationController 中定义了 flash_message_now(您可以将其添加到助手中)
def flash_message_now(type, text)
flash.now[type] ||= []
flash.now[type] << text
end

