ActionView :: Base.field_error_proc moves up the DOM tree?

Do I even have to go through the DOM tree from the html_tag element passed to?

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

Is there a way to implement this method to navigate the DOM tree and put the class in the parent div?

For example:

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

I would like to put the class on div.email, rather than putting something directly on the / input label.

Can this be done using the field_error_proc method, or is there a clean alternative?

I want to avoid this explicitly in my ideas about each field of the form. (for example, the following)

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

FYI: The short answer to my question is that there is no way to access additional parts of the DOM in the field_error_proc method. This is due to the fact that these methods do not actually create a DOM, but instead simply combine a bunch of strings. For information on some of the work options, read below.

+7
source share
1 answer

You have two options that I can come up with with my head:

Rewrite ActionView :: Base.field_error_proc

In one situation, I rewrote ActionView :: Base.field_error_proc (it has rails on it). Using a bit of nokogiri, I changed proc to add error messages to the data-error attribute of the input / textarea element, instead of wrapping them in an error div. Then he wrote a little javascript using jquery to wrap all the inputs and their labels on the document. If there is error information related to the input / textarea, it is passed to the wrapper div.

I know that this solution depends on javascript and may or may not have a graceful digression, but it works fine in my situation, as it is for a web application, not a public site. I feel good requiring javascript in this script.

 # 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 

Create your own ActionView :: Helpers :: FormBuilder

You can also get more or less the same effect by overriding the text_field method so that it always returns wrapped input. Then, using the object variable, access the error hash and add all the necessary error information to the shell. It does not require javascript and works beautifully, but in the end I prefer the first approach, because I consider it more flexible. If, however, I worked on a public site, I would use this approach.

In addition, FYI, it was convenient for me to override the check_box and radio_button methods so that they always return an input with an associated label. There are many interesting things you can do with the special 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 

The above example shows how to wrap check_box, but it more or less matches the text box. As I said, use object.errors to access error hashing, if necessary. This is just the tip of the iceberg ... there is a ton that you can do with custom FormBuilders.

If you are on the path to creating a custom form template, you might find it helpful to modify the above ActionView :: Base.field_error_proc so that you don't get double wrapped fields when errors occur.

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

To use the form constructor, specify it in a call to the form_for method or put the following in the application helper:

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

Often my decisions end up using some combination of each to achieve the desired result.

+14
source

All Articles