Rails 3:“field-with-errors”包装器改变了页面外观。如何避免这种情况?

Posted

技术标签:

【中文标题】Rails 3:“field-with-errors”包装器改变了页面外观。如何避免这种情况?【英文标题】:Rails 3: "field-with-errors" wrapper changes the page appearance. How to avoid this? 【发布时间】:2011-07-13 04:26:38 【问题描述】:

电子邮件字段:

<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>

看起来像这样:

如何避免这种外观变化?

【问题讨论】:

嗨@misha-moroshko,我尝试在父级添加错误类为described here。我尝试使用 byebug 深入研究 Rails 代码,但我立即迷失了方向。我想通过检查这些字段是否有父字段来以一种有点聪明的方式设置此行为。 【参考方案1】:

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

.field_with_errors  display: inline; 

【讨论】:

这充其量只是一个 hack,因为它否定了 html_tag 上使用的任何 display: 属性(和其他布局样式)。 我不认为它是一种黑客行为。在添加此 css 之前使用的 display 属性是 block,这会导致不希望出现的视觉差异。它不会否定标签上的任何其他布局样式。但是,如果您想更改/删除包含错误字段的标签,Ryan Bigg 的答案是完美的。 我试过这个,但是,如果你的字段在 if

标签内,它似乎不起作用(至少在 Firefox 上不起作用),因为

中的

断行无论。使用 Biggs 解决方案,仅将
【参考方案2】:

您应该覆盖ActionView::Base.field_error_proc。目前在ActionView::Base中定义为this:

 @@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 服务器以使此更改生效。

【讨论】:

一个小问题:为什么labelinput 都被包装了? Rails 如何决定包装什么? 这可能是为了让您也可以设置错误字段的标签样式。此外,rails 知道要包装什么,因为您告诉它哪些字段属于您正在为其制作表单的资源的哪个属性:@resource.errors 中的f.label :passwordf.password_field :password 将有一个[:password] 错误集。 如果您正在使用 twitter 引导程序,或者您想要另一个可以在 field_error_proc 中执行的操作的示例,请查看这个很棒的要点:gist.github.com/1464315 为什么要做 "#html_tag".html_safe 而不是 html_tag.html_safe ? Anurag:如果 html_tag 恰好为 nil,或者不是字符串,那么普通的 html_tag.html_safe 会引发错误。将其放入“#html_tag”会隐式调用 html_tag.to_s,希望它会返回一个字符串,然后该字符串将能够响应 html_safe【参考方案3】:

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 

【讨论】:

这对我来说效果很好;似乎是与 Twitter Bootstrap 一起使用的最优雅的解决方案【参考方案4】:

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

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

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

【讨论】:

这对于以不显眼的方式使用错误字段非常棒。 在 Rails 4.0.3 上工作。 为我工作。不得不重新启动 Rails 服务器以注意到更改:) 我喜欢这个解决方案,但如果您在 label 中有其他标签,它将无法工作。 嗨@Phobetron,确实这是一个不错的解决方案。我搜索了一种在父级别添加该类的方法,而不是像described here。我深入研究 Rails 代码,但我立即迷失了自己,无法使用 byebug 跟踪渲染过程。你认为这实际上可能吗?【参考方案5】:

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

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

【讨论】:

【参考方案6】:

要记住的一件事(正如我今天发现的那样)是,如果您浮动标签或输入字段(我将所有输入字段都浮动),即使您覆盖 css 也会中断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;

【讨论】:

【参考方案7】:

这是我在@Phobetron 的回答之上构建的解决方案。将此代码放在application.rb 中,由相应的form.error :p 调用生成的&lt;p&gt;&lt;span&gt; 标签将收到fields_with_errors css 标签。其余的将收到error CSS 类。

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

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

【讨论】:

【参考方案8】:

如果它只是为了造型目的(你不介意div),你可以把它添加到你的css中:

div.field_with_errors 
 display: inline;

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

【讨论】:

【参考方案9】:

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

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

  .parent_class .field_with_errors 
    display: inline;
  

它会解决问题,并且不会干扰我们应用程序中的任何其他表单。

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

.field_with_errors  display: inline;  

将进行修复。希望对你有帮助:)

【讨论】:

【参考方案10】:

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

# 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

【讨论】:

【参考方案11】:

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

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

# 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

【讨论】:

但是如果您的字段没有类属性,这将不起作用【参考方案12】:

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

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

如果inputlabel 在任何地方都有自己的class 属性 &lt;input type="my-field" class="control"&gt; &lt;label class="active" for="..."&gt;My field&lt;/label&gt; 如果inputlabel 标签没有class 属性 &lt;input type="my-field"&gt; &lt;label for="..."&gt;My field&lt;/label&gt; 如果label标签内部有另一个标签class attribute &lt;label for="..."&gt;&lt;i class="icon-name"&gt;&lt;/i&gt;My field&lt;/label&gt;

在所有这些情况下,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

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

【讨论】:

【参考方案13】:

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

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

【讨论】:

【参考方案14】:

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

$('FORM .field_with_errors > INPUT[type="checkbox"]').unwrap();

【讨论】:

【参考方案15】:

如果您根本不希望特定表单元素使用额外的&lt;div class="field_with_errors"&gt; div,您可以轻松地完全禁用它。例如。如果您不希望它用于&lt;label&gt;,请使用自定义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_fordefault_form_builder MyFormBuilder添加到您的控制器(或基本控制器,如果你想要它的全局行为)。

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

感谢 Jack Casey 提供此答案。

【讨论】:

以上是关于Rails 3:“field-with-errors”包装器改变了页面外观。如何避免这种情况?的主要内容,如果未能解决你的问题,请参考以下文章

Rails:“bundle install”由于旧的 rails 版本(3.2)而失败

Rails 3.2.3 GeoLocation 使用 MaxMind

从 Rails 2 到 Rails 3 路由

在使用 Rails 3 时遇到一些问题,继续使用 Rails 4

如何在 Rails 6.0.3.3 中使用 jQuery?

通过 ajax rails 2 向 rails 3 提交表单