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

电邮地址:

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

看起来像这样:

without_error

但是,如果电子邮件验证失败,它就会变成:

<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

我怎样才能避免这种外表的改变呢?

50155 次浏览

由于 div元素是块元素,所以正在发生您所看到的视觉差异。将这个样式添加到您的 CSS 文件中,使其表现得像一个内联元素:

.field_with_errors { display: inline; }

你应该覆盖 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 服务器以使此更改生效。

额外的代码由 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 }

或者,您可以将其更改为适合您的用户界面的内容:

config.action_view.field_error_proc = Proc.new { |html_tag, instance| "<span class='field_with_errors'>#{html_tag}</span>".html_safe }

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

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 2上工作(像我一样) ,请查看 SOPOST给你

它提供了一个放入初始化器的脚本。

需要记住的一点是(正如我今天通过这个工作发现的) ,如果你浮动标签或输入字段(我正在浮动所有的输入字段) ,即使你覆盖 ActionView: : Base.field _ error _ proc,css 也会中断。

另一种方法是像下面这样在 CSS 格式中下降一个级别:

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


.field_with_errors input[type="text"] {
padding: 3px 2px;
border: 2px solid red;
}

这是我的解决方案建立在@Phobetron 的答案之上。将此代码放置在 application.rb中,由相应的 form.error :p调用生成的 <p><span>标记将接收到 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
}

我发现这种方式最灵活,最不引人注目的所有以前的风格的回应跨我的形式。

如果它只是为了样式的目的(你不介意的 div) ,你可以只添加这个到你的 css:

div.field_with_errors {
display: inline;
}

div将像 span一样工作,它不会干扰你的设计(因为 div是一个块元素-display: block;-默认情况下,它将导致一个新的线后,它关闭; spaninline,所以它不会)。

如果只是关于样式问题,我们可以覆盖“ field _ with _ error”。但是,由于这可能会影响应用程序中的其他表单,所以最好只用该表单覆盖“ field _ with _ error”类。

考虑到‘ father _ class’是表单错误字段的父类之一(表单的类或错误字段的任何父元素的类) ,那么

  .parent_class .field_with_errors {
display: inline;
}

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

或者

如果我们需要覆盖整个应用程序的“ field _ with _ error”样式,那么正如@donangg 所说,

.field_with_errors { display: inline; }

希望对你有所帮助:)

我做了一个选项,为一些对象禁用这个可怕的东西

# 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

除了@phobetron 应答之外,当您有其他具有 class 属性的标记(如 <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

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

enter image description here

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

  • 如果 inputlabel在任何地方都有自己的 class属性
    • <input type="my-field" class="control">
    • <label class="active" for="...">My field</label>
  • 如果 inputlabel标记没有 class属性
    • <input type="my-field">
    • <label for="...">My field</label>
  • 如果 label标记中有另一个与 class attribute在一起的标记
    • <label for="..."><i class="icon-name"></i>My field</label>

在所有这些情况下,如果 class属性中的类存在,那么 error类将被添加到现有的类中; 如果 标签输入标记中没有 error类,那么将创建 error类。

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

我希望它能对像我这样条件相同的人有所帮助。

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

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

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

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

如果您不希望对于特定的表单元素使用额外的 <div class="field_with_errors"> div,可以很容易地完全禁用它。例如,如果你不想它的 <label>的,使用一个自定义的 造型师

例如:

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