134

电子邮件字段:

<label for="job_client_email">Email: </label> 
<input type="email" name="job[client_email]" id="job_client_email">

看起来像这样:

没有错误

但是,如果电子邮件验证失败,它将变为:

<div class="field_with_errors">
  <label for="job_client_email">Email: </label>
</div> 
<div class="field_with_errors">
  <input type="email" value="wrong email" name="job[client_email]" id="job_client_email">
</div>

看起来像这样:

with_error

我怎样才能避免这种外观变化?

4

15 回答 15

238

你应该覆盖ActionView::Base.field_error_proc. 它目前在以下范围内定义为ActionView::Base

 @@field_error_proc = Proc.new{ |html_tag, instance| 
   "<div class=\"field_with_errors\">#{html_tag}</div>".html_safe
 }

您可以通过将其放在应用程序的类中来覆盖它config/application.rb

config.action_view.field_error_proc = Proc.new { |html_tag, instance| 
  html_tag
}

重新启动 rails 服务器以使此更改生效。

于 2011-03-11T01:45:42.657 回答
102

您看到的视觉差异正在发生,因为该div元素是块元素。将此样式添加到您的 CSS 文件中,使其行为类似于内联元素:

.field_with_errors { display: inline; }
于 2011-03-11T01:38:35.200 回答
72

我目前使用这个解决方案,放在一个初始化程序中:

ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
  class_attr_index = html_tag.index 'class="'

  if class_attr_index
    html_tag.insert class_attr_index+7, 'error '
  else
    html_tag.insert html_tag.index('>'), ' class="error"'
  end
end

这使我只需将类名添加到适当的标记中,而无需创建其他元素。

于 2011-12-05T02:29:20.657 回答
21

额外的代码正在添加ActionView::Base.field_error_proc。如果您不使用field_with_errors样式设置表单,则可以在以下位置覆盖它application.rb

config.action_view.field_error_proc = Proc.new { |html_tag, instance| html_tag.html_safe }

或者,您可以将其更改为适合您的 UI 的内容:

config.action_view.field_error_proc = Proc.new { |html_tag, instance| "<span class='field_with_errors'>#{html_tag}</span>".html_safe }
于 2011-03-11T01:46:24.760 回答
5

我正在使用 Rails 5 和Materialize-Sass,我在 Rails 的默认行为中遇到了一些问题,即如下图所示处理失败的字段验证,这是因为div在验证失败的输入字段中添加了额外的内容。

在此处输入图像描述

使用@Phobetron 回答并修改 Hugo Demiglio 的回答。我对这些代码块进行了一些调整,在以下情况下我得到了一些运行良好的东西:

  • 如果两者input和在任何地方label都有自己的class属性
    • <input type="my-field" class="control">
    • <label class="active" for="...">My field</label>
  • 如果inputorlabel标签没有class属性
    • <input type="my-field">
    • <label for="...">My field</label>
  • 如果label标签里面有另一个标签class attribute
    • <label for="..."><i class="icon-name"></i>My field</label>

在所有这些情况下,如果存在,error该类将添加到class属性中的现有类中,如果标签输入标签中不存在该类,则将创建该类。

ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
    class_attr_index = html_tag.index('class="')
    first_tag_end_index = html_tag.index('>')

    # Just to inspect variables in the console
    puts ' ' * 50
    pp(html_tag)
    pp(class_attr_index)
    pp(first_tag_end_index)

    if class_attr_index.nil? || class_attr_index > first_tag_end_index
        html_tag.insert(first_tag_end_index, ' class="error"')
    else
        html_tag.insert(class_attr_index + 7, 'error ')
    end

    # Just to see resulting tag in the console
    pp(html_tag)
end

我希望它对像我一样条件相同的人有用。

于 2017-01-30T05:54:38.827 回答
4

除了@phobetron 答案,当您有其他具有类属性的标签时,它不起作用,比如<label for="..."><i class="icon my-icon"></i>My field</label>.

我对他的解决方案做了一些更改:

# config/initializers/field_with_error.rb

ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
  class_attr_index = html_tag.index('class="')
  first_tag_end_index = html_tag.index('>')

  if class_attr_index.nil? || first_tag_end_index > class_attr_index
    html_tag.insert(class_attr_index + 7, 'error ')
  else
    html_tag.insert(first_tag_end_index, ' class="error"')
  end
end
于 2016-03-28T20:02:11.380 回答
2

如果由于某种原因您仍在使用 Rails 2(就像我一样),请查看此处的 SO 帖子。

它提供了一个脚本来放入初始化程序。

于 2012-05-30T20:16:33.187 回答
2

要记住的一件事(正如我今天发现的那样)是,如果您浮动标签或输入字段(我将所有输入字段浮动),即使您覆盖 ActionView:: Base.field_error_proc。

另一种方法是在 CSS 格式中更深一层,如下所示:

.field_with_errors label {
  padding: 2px;
  background-color: red;
}

.field_with_errors input[type="text"] {
  padding: 3px 2px;
  border: 2px solid red;
}
于 2012-06-06T19:53:23.333 回答
2

我为某些对象选择了禁用这个可怕的东西

# config/initializers/field_error_proc.rb

module ActiveModel::Conversion
  attr_accessor :skip_field_error_wrapper
end

ActionView::Base.field_error_proc = Proc.new {|html_tag, instance|
  if instance.object && instance.object.skip_field_error_wrapper
    html_tag.html_safe
  else
    "<div class=\"field_with_errors\">#{html_tag}</div>".html_safe
  end
}

所以可以这样使用它:

@user.skip_field_error_wrapper = true
form_for(@user) do |f|
  ...
end
于 2015-01-15T06:29:33.217 回答
1

这是我在@Phobetron 的回答之上构建的解决方案。将此代码放入application.rb,您的<p><span>相应form.error :p调用生成的标签将收到fields_with_errorscss 标签。其余的将收到errorCSS 类。

config.action_view.field_error_proc = Proc.new { |html_tag, instance|
  class_attr_index = html_tag.index 'class="'

  if class_attr_index
    # target only p's and span's with class error already there
    error_class = if html_tag =~ /^<(p|span).*error/
      'field_with_errors '
    else
      'error '
    end

    html_tag.insert class_attr_index + 7, error_class
  else
    html_tag.insert html_tag.index('>'), ' class="error"'
  end
}

我发现这种方式在我的表单中设置响应样式是以前所有方式中最灵活和最不引人注目的。

于 2013-11-11T13:51:18.870 回答
1

如果它只是用于样式目的(您不介意div),您可以将其添加到您的 css 中:

div.field_with_errors {
 display: inline;
}

div像 a 一样span,它不会干扰您的设计(因为div是块元素——display: block;默认情况下,它会在关闭后产生一个新行;spaninline,所以它不会)。

于 2014-03-28T14:29:22.040 回答
1

如果您只想关闭某些元素的错误,例如checkboxes,您可以这样做:

ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
  doc = Nokogiri::HTML::Document.parse(html_tag)
  if doc.xpath("//*[@type='checkbox']").any?
    html_tag
  else
    "<div class=\"field_with_errors\">#{html_tag}</div>".html_safe
  end
end
于 2019-08-21T16:53:58.167 回答
0

如果它只是关于样式问题,我们可以覆盖“field_with_errors”。但由于这可能会影响我们应用程序中的其他表单,因此最好仅使用该表单覆盖“field_with_errors”类。

考虑到'parent_class'是表单错误字段的父类之一(表单的类或错误字段的任何父元素的类),然后

  .parent_class .field_with_errors {
    display: inline;
  }

它将解决这个问题,它也不会干扰我们应用程序中的任何其他形式。

或者

如果我们需要为整个应用程序覆盖“field_with_errors”的样式,那么正如@dontangg 所说,

.field_with_errors { display: inline; } 

将进行修复。希望能帮助到你 :)

于 2014-04-21T12:00:30.043 回答
0

如果您不想field_error_proc为整个应用程序进行更改,jQuery 的unwrap可以为特定问题区域提供更有针对性的解决方案,例如,

$('FORM .field_with_errors > INPUT[type="checkbox"]').unwrap();
于 2020-06-11T20:49:03.377 回答
0

<div class="field_with_errors">如果您根本不希望特定表单元素使用额外的 div,则可以轻松地完全禁用它。例如,如果您不希望它用于<label>'s,请使用自定义FormBuilder

例如:

class MyFormBuilder < ActionView::Helpers::FormBuilder
  # Strip the containing div for labels associated with invalid fields:
  def label(method, text = nil, options = {}, &block)
    super(method, text, options, &block).gsub(%r{<div.*?>|<\/div>}, '').html_safe
  end
end

然后要么添加, builder: MyFormBuilder到您的form_with/form_for在视图中,或者添加default_form_builder MyFormBuilder到您的控制器(或者如果您想要它的全局行为,则添加到基本控制器)。

您也可以对输入和其他表单元素执行类似操作。

感谢Jack Casey的回答。

于 2020-08-06T02:14:19.123 回答