`
Hooopo
  • 浏览: 328979 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

可定制的Rails错误回显

阅读更多
通常rails页面的错误信息提示都是放在首部用 error_message_for,生成这样的错误提示:


这样很快就把所有错误信息都显示出来了。但是有一个缺点,就是灵活性太差。感觉error_message_for就像rails的scaffold一样可以快速搭建一个curd应用,但是定制性不高。

还好,rails的可扩展能力是很强的,你不喜欢里面的某一方法,你甚至可以去把他的源代码改了(当然不推荐这样做...),所以只有自己动手..

看了下源码:

# Returns the Errors object that holds all information about attribute error messages.
    def errors
      @errors ||= Errors.new(self)
   end


这一句就够了:原来每个ActiveRecord的实例都有一个errors方法。这个@errors就是所有rails其他错误信息处理封装的基础。

自己写了一个对每个model的field错误信息提取到div的方法,同时把汉化也加进去了,很简单。。
module ApplicationHelper
  def error_div(model, field, field_name)
    return unless model
    field = field.is_a?(Symbol) ? field.to_s : field
    errors = model.errors[field]
    return unless errors
    %Q(
    <div class="errors">
    #{errors.is_a?(Array) ? errors.map{|e| field_name + e}.join(",") : field_name << errors}
    </div>
    )
  end
end



demo:
validation:
class Post < ActiveRecord::Base
  validates_presence_of :title, :message => "不能为空"
  validates_length_of   :title, :in => 2..10, :message => "长度不正确"
end


view:

<% form_for(@post) do |f| %>

  <p>
    <%= f.label :title %><br />
    <%= f.text_field :title %><%= error_div(@post, :title, "标题")%>
  </p>
  <p>
    <%= f.label :description %><br />
    <%= f.text_area :description %>
  </p>
  <p>
    <%= f.submit 'Create' %>
  </p>
<% end %>


效果1:

效果2:



分享到:
评论
22 楼 lzqustc 2009-09-23  
<p>class User &lt; ActiveRecord::Base<br>   validates_presence_of     :email,  :message =&gt; "邮箱不能为空!"<br>   ......<br>end<br><br> %tr<br>     %td{ :height =&gt; "30", :align =&gt; "right", :valign =&gt; "middle" }<br>          %strong{:style =&gt;"color: red;"}*<br>           E-mail:<br>      %td{ :height =&gt; "30", :align =&gt; "left", :valign =&gt; "middle" }<br>           = user_form.text_field :email , :size =&gt; "20"<br>          <span style="color: #ff0000;">  %span{:style =&gt;"color: red;"}<br>            = @user.errors[:email]</span></p>
<p>效果如下:</p>
<p> </p>
<p><img src="http://dl.iteye.com/upload/attachment/149600/691489b4-5c20-3053-a3be-19a214b2f83e.bmp" alt=""></p>
<p> </p>
<p>不知,大家觉得如何?</p>
21 楼 星情泪 2009-09-20  
<div class="quote_title">amonlei 写道</div>
<div class="quote_div">
<p>8.3 Customizing the Error Messages HTML<br><br>By default, form fields with errors are displayed enclosed by a div element with the fieldWithErrors CSS class. However, it’s possible to override that.<br><br>The way form fields with errors are treated is defined by ActionView::Base.field_error_proc. This is a Proc that receives two parameters:<br><br>    * A string with the HTML tag<br>    * An instance of ActionView::Helpers::InstanceTag.<br><br>Here is a simple example where we change the Rails behaviour to always display the error messages in front of each of the form fields with errors. The error messages will be enclosed by a span element with a validation-error CSS class. There will be no div element enclosing the input element, so we get rid of that red border around the text field. You can use the validation-error CSS class to style it anyway you want.<br>ActionView::Base.field_error_proc = Proc.new do |html_tag, instance| if instance.error_message.kind_of?(Array) %(#{html_tag}&lt;span class="validation-error"&gt;&amp;nbsp; #{instance.error_message.join(',')}&lt;/span&gt;) else %(#{html_tag}&lt;span class="validation-error"&gt;&amp;nbsp; #{instance.error_message}&lt;/span&gt;) end end<br><br>This will result in something like the following:</p>
<p><img src="http://guides.rubyonrails.org/images/validation_error_messages.png" alt="" width="410" height="53"><br><br>Validation error messages</p>
<p> </p>
</div>
<p> </p>
<p>非常棒!这样以后再写模型验证的时候,就可以把同一类型的写到一块了。</p>
<p>以前总是这么写</p>
<pre name="code" class="ruby">validates_numericality_of :weight, :message =&gt; "重量 必须是数字"
validates_numericality_of :num, :message =&gt; "购买数量 必须是数字"</pre>
<p> 虽然验证的内容是一个类型的,但是因为字段名不一样,所以还得自己加上,现在可以全部加到一起了</p>
<pre name="code" class="ruby">validates_numericality_of :num, :weight, :message =&gt; "必须是数字"</pre>
<p> 反正错误信息会出现到表单元素的后面,方便很多,呵呵</p>
20 楼 amonlei 2009-09-17  
Hooopo 写道
amonlei 写道
很明显,楼主没有去阅读过 rails文档

文档都是用来查的.....
原来是error_message_on啊!
引用

error_message_on(object, method, *args)
Returns a string containing the error message attached to the method on the object if one exists. This error message is wrapped in a DIV tag, which can be extended to include a :prepend_text and/or :append_text (to properly explain the error), and a :css_class to style it accordingly. object should either be the name of an instance variable or the actual object. The method can be passed in either as a string or a symbol. As an example, let’s say you have a model @post that has an error message on the title attribute:

  <%= error_message_on "post", "title" %>
  # => <div class="formError">can't be empty</div>

  <%= error_message_on @post, :title %>
  # => <div class="formError">can't be empty</div>

  <%= error_message_on "post", "title",
      :prepend_text => "Title simply ",
      :append_text => " (or it won't work).",
      :css_class => "inputError" %>




<%= error_message_on "post", "title", 
      :prepend_text => "Title simply ", 
      :append_text => " (or it won't work).", 
      :css_class => "inputError" %> 

啊,土了,,原来error_message_on这个轮子已经造好了。
貌似ActionView::Base.field_error_proc也很不错。

文档不是用来查的,平时阅读以下,知道有哪些方面的内容,不然真是书到用时方恨少
19 楼 Hooopo 2009-09-16  
amonlei 写道
很明显,楼主没有去阅读过 rails文档

文档都是用来查的.....
原来是error_message_on啊!
引用

error_message_on(object, method, *args)
Returns a string containing the error message attached to the method on the object if one exists. This error message is wrapped in a DIV tag, which can be extended to include a :prepend_text and/or :append_text (to properly explain the error), and a :css_class to style it accordingly. object should either be the name of an instance variable or the actual object. The method can be passed in either as a string or a symbol. As an example, let’s say you have a model @post that has an error message on the title attribute:

  <%= error_message_on "post", "title" %>
  # => <div class="formError">can't be empty</div>

  <%= error_message_on @post, :title %>
  # => <div class="formError">can't be empty</div>

  <%= error_message_on "post", "title",
      :prepend_text => "Title simply ",
      :append_text => " (or it won't work).",
      :css_class => "inputError" %>




<%= error_message_on "post", "title", 
      :prepend_text => "Title simply ", 
      :append_text => " (or it won't work).", 
      :css_class => "inputError" %> 

啊,土了,,原来error_message_on这个轮子已经造好了。
貌似ActionView::Base.field_error_proc也很不错。
18 楼 amonlei 2009-09-16  
<p>8.3 Customizing the Error Messages HTML<br><br>By default, form fields with errors are displayed enclosed by a div element with the fieldWithErrors CSS class. However, it’s possible to override that.<br><br>The way form fields with errors are treated is defined by ActionView::Base.field_error_proc. This is a Proc that receives two parameters:<br><br>    * A string with the HTML tag<br>    * An instance of ActionView::Helpers::InstanceTag.<br><br>Here is a simple example where we change the Rails behaviour to always display the error messages in front of each of the form fields with errors. The error messages will be enclosed by a span element with a validation-error CSS class. There will be no div element enclosing the input element, so we get rid of that red border around the text field. You can use the validation-error CSS class to style it anyway you want.<br>ActionView::Base.field_error_proc = Proc.new do |html_tag, instance| if instance.error_message.kind_of?(Array) %(#{html_tag}&lt;span class="validation-error"&gt;&amp;nbsp; #{instance.error_message.join(',')}&lt;/span&gt;) else %(#{html_tag}&lt;span class="validation-error"&gt;&amp;nbsp; #{instance.error_message}&lt;/span&gt;) end end<br><br>This will result in something like the following:</p>
<p><img src="http://guides.rubyonrails.org/images/validation_error_messages.png" alt="" width="410" height="53"><br><br>Validation error messages</p>
<p> </p>
17 楼 amonlei 2009-09-16  
很明显,楼主没有去阅读过 rails文档
16 楼 whaosoft 2009-09-16  
yearl 写道
这样分离显示的界面更友好。

同意
15 楼 night_stalker 2009-09-16  
Hooopo 写道

不过你这个方案(I18N+error_message_on+一堆配置,貌似还有判断locale和在view里加t)有点杀鸡用牛刀的感觉了。。
大多数时候人家只是要一个可以正确显示中文的页面..


i18n 是 rails 2.2 才集成的。不用白不用。

没几个网站会有多语言显示的奇怪要求的 …… 不用在 view 判断 locale, 直接在 environment.rb 中设 zh-cn 就行了。
activerecord 默认载入 en-us.yml (可以在 activerecord 的目录里找到),不改 locale,直接把 en-us.yml 的内容替换掉会更高效和省内存(可以省 50k !……)
14 楼 one23 2009-09-16  
直接用QuakeWang的ActionView::Base.field_error_proc就可以省你很多了,我自己业余的应用用这个的
ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
  if instance.error_message.kind_of?(Array)
    %(#{html_tag}<span class="validation_error">&nbsp;  #{instance.error_message.first}</span>)
  else
    %(#{html_tag}<span class="validation_error">&nbsp;  #{instance.error_message}</span>)
  end
end

13 楼 Hooopo 2009-09-16  
night_stalker 写道
Hooopo 写道

这只是汉化,但是还没有解决我想把email域的错误信息放在email文本框右边或下面的需求,而且这个需求也是完全正当的...


啦啦啦:

<%= error_message_on @user, :email %>


如果出错就会产生:

<div class="formError">伊妹儿不能为空</div>

居然这样也行,学习了..

不过你这个方案(I18N+error_message_on+一堆配置,貌似还有判断locale和在view里加t)有点杀鸡用牛刀的感觉了。。
大多数时候人家只是要一个可以正确显示中文的页面..
12 楼 QuakeWang 2009-09-16  
JavaEye的做法是自定义field_error_proc,配合前端的js和css,能够在后台出错的时候和前段校验有一样的效果:
ActionView::Base.field_error_proc = Proc.new {|html_tag, instance|
  %(
  #{html_tag}
  <script type="text/javascript">
    var elm = $('#{instance.object_name}_#{instance.method_name}');
  elm.tooltip = new Tooltip(elm, {backgroundColor: "#FC9", borderColor: "#C96", textColor: "#000", textShadowColor: "#FFF"});
  elm.tooltip.content = "#{instance.error_message.kind_of?(Array) ? instance.error_message.join(',') : instance.error_message}"
  elm.addClassName('validation-failed');
  try{elm.focus();}catch(e){};
  </script>
  )
11 楼 Hooopo 2009-09-16  
yuan 写道
我的想法一直是这样的:我打算直接不理这个验证错误的提示,反正前端有js挡着,就不用费功夫去搞这些了。model验证我认为主要是保证数据的正确性,而js的验证则是为了用户体验。对于那种能想办法绕过前端验证的家伙,就无所谓什么用户体验了吧。对于用web却禁掉js的家伙直接不予支持。

我觉得没有什么问题,大家认为如何?

1.去不去搞和能不能搞是两个概念,当然这是针对那些说rails封装过度的人说的..
2.并不是所有禁用js的人都是要绕过你的js去做坏事,在浏览器里还有一个选项是“禁用js”之前,禁用js的用户就是合法用户。
3.有些设备不支持js,比如:
引用
JE改版后博客和圈子用了Ajax,貌似体验好了很多,但是我在用手机浏览的时候就无法回复。这一点《敏捷web开发》那本书做的就很好,在遇到不支持ajax的设备提供了普通的post方法,保证在所有设备上是可用的。
10 楼 night_stalker 2009-09-16  
Hooopo 写道

这只是汉化,但是还没有解决我想把email域的错误信息放在email文本框右边或下面的需求,而且这个需求也是完全正当的...


啦啦啦:

<%= error_message_on @user, :email %>


如果出错就会产生:

<div class="formError">伊妹儿不能为空</div>
9 楼 Hooopo 2009-09-16  
night_stalker 写道
貌似用 i18n 也可以定制 …… 而且更简单些:

environment.rb:
  config.i18n.default_locale = 'zh-cn'

2.给 ActiveRecord 加个中文 locale,控制错误消息的格式。

3.给 model 配置每个字段的翻译。

2 和 3 通过在 config/locales 里面添加 zh-cn.xxxxxx.yml 完成。最后输出的错误信息就是中文的并且合符格式的了。

居然修的这么快...
这只是汉化,但是还没有解决我想把email域的错误信息放在email文本框右边或下面的需求,而且这个需求也是完全正当的...
8 楼 night_stalker 2009-09-16  
- -,再添加这个:
zh-cn:
  users:
    name: 法号
    password: 芝麻开门

就显示为:

芝麻开门与确认值不匹配
法号已经被使用

不用每个字段都加 :message => ...
7 楼 yuan 2009-09-16  
我的想法一直是这样的:我打算直接不理这个验证错误的提示,反正前端有js挡着,就不用费功夫去搞这些了。model验证我认为主要是保证数据的正确性,而js的验证则是为了用户体验。对于那种能想办法绕过前端验证的家伙,就无所谓什么用户体验了吧。对于用web却禁掉js的家伙直接不予支持。

我觉得没有什么问题,大家认为如何?
6 楼 Hooopo 2009-09-16  
  activerecord:
    errors:
      template:
        header:
          one: "有 1 个错误发生导致「{{model}}」无法被保存。"
          other: "有 {{count}} 个错误发生导致「{{model}}」无法被保存。"
        body: "如下字段出现错误:"
      messages:
        inclusion: "不包含于列表中"
        exclusion: "是保留关键字"
        invalid: "是无效的"
        confirmation: "与确认值不匹配"
        accepted: "必须是可被接受的"
        empty: "不能留空"
        blank: "不能为空字符"
        too_long: "过长(最长为 {{count}} 个字符)"
        too_short: "過短(最短为 {{count}} 个字符)"
        wrong_length: "长度非法(必须为 {{count}} 个字符)"
        taken: "已经被使用"
        not_a_number: "不是数字"
        greater_than: "必须大于 {{count}}"
        greater_than_or_equal_to: "必须大于或等于 {{count}}"
        equal_to: "必须等于 {{count}}"
        less_than: "必须小于 {{count}}"
        less_than_or_equal_to: "必须小于或等于 {{count}}"
        odd: "必须为单数"
        even: "必须为双数"



哈哈,ns估计又没仔细看帖.......
这部分只是把默认的错误信息换成中文,但是会出现这样囧的错误提示:
引用

password与确认值不匹配
name已经被使用
...


这个只要在validation上面加:message选项就可以了
validates_presence_of :title, :message => "不能为空"

5 楼 night_stalker 2009-09-16  
Hooopo 写道
night_stalker 写道
貌似用 i18n 也可以定制 …… 而且更简单些

我只知道I18N可以把field名字国际化,但是如何使错误信息在页面布局可控还不知道怎么做


zh-cn.yml: 扔进 config/locales 里面
# Chinese (China) translations for Ruby on Rails
# by tsechingho (http://github.com/tsechingho)

...
...

  activerecord:
    errors:
      template:
        header:
          one: "有 1 个错误发生导致「{{model}}」无法被保存。"
          other: "有 {{count}} 个错误发生导致「{{model}}」无法被保存。"
        body: "如下字段出现错误:"
      messages:
        inclusion: "不包含于列表中"
        exclusion: "是保留关键字"
        invalid: "是无效的"
        confirmation: "与确认值不匹配"
        accepted: "必须是可被接受的"
        empty: "不能留空"
        blank: "不能为空字符"
        too_long: "过长(最长为 {{count}} 个字符)"
        too_short: "過短(最短为 {{count}} 个字符)"
        wrong_length: "长度非法(必须为 {{count}} 个字符)"
        taken: "已经被使用"
        not_a_number: "不是数字"
        greater_than: "必须大于 {{count}}"
        greater_than_or_equal_to: "必须大于或等于 {{count}}"
        equal_to: "必须等于 {{count}}"
        less_than: "必须小于 {{count}}"
        less_than_or_equal_to: "必须小于或等于 {{count}}"
        odd: "必须为单数"
        even: "必须为双数"
4 楼 Hooopo 2009-09-16  
night_stalker 写道
貌似用 i18n 也可以定制 …… 而且更简单些

我只知道I18N可以把field名字国际化,但是如何使错误信息在页面布局可控还不知道怎么做
3 楼 yearl 2009-09-16  
这样分离显示的界面更友好。

相关推荐

Global site tag (gtag.js) - Google Analytics