Rails - ActionView :: Base.field_error_proc向上移动DOM树?

时间:2011-09-07 23:16:27

标签: ruby-on-rails ruby parsing dom actionview

无论如何都要从传入的html_tag元素中获取DOM树吗?

ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
  # implementation
end

无论如何我可以实现这个方法来提升DOM树并在父div上放置一个类吗?

例如:

<div class="email">
  <label for="user_email">Email Address</label>
  <input id="user_email" name="user[email]" size="30" type="text" value="">
</div>

我想在div.email上放一个类,而不是直接在输入/标签上放置一些东西。

可以使用field_error_proc方法完成,还是有一个干净的替代方法?

我想避免在每个表单字段的视图中明确地这样做。 (如下所示)

.email{:class => object.errors[:email].present? 'foo' : nil}
  =form.label :email
  =form.text_field :email

仅供参考:对我的问题的简短回答是,无法在field_error_proc方法中访问DOM的其他部分。这是因为这些方法实际上并没有构建DOM,而只是将一堆字符串连接在一起。有关可能的解决方法的信息,请阅读以下解决方案。

1 个答案:

答案 0 :(得分:15)

我有两种选择可以让我想到:

重写ActionView :: Base.field_error_proc

在一种情况下,我重写了ActionView :: Base.field_error_proc(它上面有一个rails)。使用一点nokogiri,我更改了proc,将错误消息添加到input / textarea元素的data-error属性,而不是将它们包装在错误div中。然后,使用jquery写了一个小javascript,将所有输入及其标签包装在文档就绪上。如果存在与输入/ textarea关联的错误信息,则会将其传送到包装器div。

我知道这个解决方案依赖于javascript,并且可能有也可能没有优雅的回退,但它在我的情况下工作正常,因为它适用于Web应用程序而不是可公开访问的站点。我认为在那种情况下需要javascript。

# place me inside your base controller class
ActionView::Base.field_error_proc = Proc.new do |html_tag, object|
  html = Nokogiri::HTML::DocumentFragment.parse(html_tag)
  html = html.at_css("input") || html.at_css("textarea")
  unless html.nil?
    css_class = html['class'] || "" 
    html['class'] = css_class.split.push("error").join(' ')
    html['data-error'] = object.error_message.join(". ")
    html_tag = html.to_s.html_safe
  end
  html_tag
end

编写自己的ActionView :: Helpers :: FormBuilder

通过覆盖text_field方法,您也可以获得或多或少相同的效果,以便它始终返回包装的输入。然后,使用对象变量,访问错误哈希并将任何所需的错误信息添加到包装器。这个不需要javascript并且运行良好,但最后我更喜欢第一种方法,因为我发现它更灵活。但是,如果我在一个可公开访问的网站上工作,我会改用这种方法。

另外,仅供参考,我发现覆盖check_box和radio_button方法很方便,因此它们总是返回带有相关标签的输入。使用自定义FormBuilder可以做很多有趣的事情。

# place me inside your projects lib folder
class PrettyFormBuilder < ActionView::Helpers::FormBuilder  
  def check_box(field, label_text, options = {})
    checkbox = super(field, options)
    checkbox += label(field, label_text)
    @template.content_tag(:div, checkbox, :class => "wrapper")
  end
end

以上示例显示了如何包装check_box,但它与text_field大致相同。就像我说的,如果需要,使用object.errors来访问错误哈希。这只是冰山的一角......你可以用自定义的FormBuilders做很多事。

如果你去自定义表单构建器路由,你可能会发现修改上面的ActionView :: Base.field_error_proc如下所示是有帮助的,所以当出现错误时你不会得到双重包装的字段。

ActionView::Base.field_error_proc = Proc.new do |html_tag, object|
  html_tag # return the html tag unmodified and unwrapped
end

要使用表单构建器,请在form_for方法调用中指定它,或将以下内容放在应用程序助手中:

# application helper
module ApplicationHelper
  ActionView::Base.default_form_builder = PrettyFormBuilder
end

我的解决方案通常最终会使用每种解决方案的组合来实现所需的结果。