问题
Email field:
<label for=\"job_client_email\">Email: </label>
<input type=\"email\" name=\"job[client_email]\" id=\"job_client_email\">
looks like this:
But, if the email validation fails, it becomes:
<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>
which looks like this:
How could I avoid this appearance change ?
回答1:
You should override ActionView::Base.field_error_proc
. It's currently defined as this within ActionView::Base
:
@@field_error_proc = Proc.new{ |html_tag, instance|
"<div class=\"field_with_errors\">#{html_tag}</div>".html_safe
}
You can override it by putting this in your application's class inside config/application.rb
:
config.action_view.field_error_proc = Proc.new { |html_tag, instance|
html_tag
}
Restart rails server for this change to take effect.
回答2:
The visual difference you are seeing is happening because the div
element is a block element. Add this style to your CSS file to make it behave like an inline element:
.field_with_errors { display: inline; }
回答3:
I currently use this solution, placed in an initializer:
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
This allows me to merely add a class name to the appropriate tag, without creating additional elements.
回答4:
The extra code is being added by ActionView::Base.field_error_proc
. If you're not using field_with_errors
to style your form, you can override it in application.rb
:
config.action_view.field_error_proc = Proc.new { |html_tag, instance| html_tag.html_safe }
Alternatively, you can change it to something that suits your UI:
config.action_view.field_error_proc = Proc.new { |html_tag, instance| "<span class='field_with_errors'>#{html_tag}</span>".html_safe }
回答5:
In addition of @phobetron answer, which doesn't work when you have other tag with class attribute, like <label for="..."><i class="icon my-icon"></i>My field</label>
.
I did some changes on his solution:
# 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
回答6:
I am working with Rails 5 and Materialize-Sass and I am getting some issues with the default behavior from Rails to treat failed field validations as in the image below and it was because of the extra div
added to the input fields where validation failed.
Working with @Phobetron answer and modifying Hugo Demiglio's answer too. I made some adjustments to those blocks of code and I get something working well in the following cases:
- If both
input
andlabel
has their ownclass
attribute anywhere<input type="my-field" class="control">
<label class="active" for="...">My field</label>
- If the
input
orlabel
tags does not have aclass
attribute<input type="my-field">
<label for="...">My field</label>
- if the
label
tag has another tag inside with theclass attribute
<label for="..."><i class="icon-name"></i>My field</label>
In all those cases the error
class will be added to the existing classes in the class
attribute if exist or it will be created if it is not present in the label or input tags.
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
I hope it could be useful for someone with the same conditions like me.
回答7:
If for some reason you are still working on Rails 2 (like I am) check out the SO post here.
It offers a script to put in initializers.
回答8:
One thing to keep in mind (as I discovered working through this today) is that if you float either the label or input fields (I'm floating all of the input fields right), the css will break even if you override ActionView::Base.field_error_proc.
An alternative is to drop a level deeper in the CSS formatting like so:
.field_with_errors label {
padding: 2px;
background-color: red;
}
.field_with_errors input[type="text"] {
padding: 3px 2px;
border: 2px solid red;
}
回答9:
I made an option to disable this terrible thing for some objects
# 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
}
So can use it like this:
@user.skip_field_error_wrapper = true
form_for(@user) do |f|
...
end
回答10:
This is my solution building on top of @Phobetron's answer. Placing this code in application.rb
, your <p>
and <span>
tags generated by the corresponding form.error :p
calls will receive the fields_with_errors
css tag. The rest will receive the error
CSS class.
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
}
I found this way the most flexible and unobstrusive of all previous to style the response across my forms.
回答11:
If it's just for styling purposes (you don't mind the div
), you can just add this to your css:
div.field_with_errors {
display: inline;
}
The div
will act like a span
and it won't interfere with your design (since div
is a block element – display: block;
– by default, it will cause a new line after it closes; span
is inline
, so it does not).
回答12:
If its only about styling issues, we can overwrite "field_with_errors". But as that might effect other forms in our application, it's better to overwrite the "field_with_errors" class with in that form only.
Considering 'parent_class' is one of the parent class for form's error field (either form's class or class of any of the parent element for error field), then
.parent_class .field_with_errors {
display: inline;
}
It will fix the issue as well as, it won't disturb any other forms in our applicaiton as well.
OR
If we need to override the style of "field_with_errors" for whole applicaiton, then as @dontangg said,
.field_with_errors { display: inline; }
will do the fix. Hope it helps :)
回答13:
If you just want to turn off errors for certain elements, e.g. checkboxes, you can do this:
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
来源:https://stackoverflow.com/questions/5267998/rails-3-field-with-errors-wrapper-changes-the-page-appearance-how-to-avoid-t