Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Customize Field Errors with Rails 5 and Bootstrap
# Adapted from https://rubyplus.com/articles/3401-Customize-Field-Error-in-Rails-5
ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
html = ''
form_fields = [
'textarea',
'input',
'select'
]
elements = Nokogiri::HTML::DocumentFragment.parse(html_tag).css "label, " + form_fields.join(', ')
elements.each do |e|
if e.node_name.eql? 'label'
html = %(#{e}).html_safe
elsif form_fields.include? e.node_name
e['class'] += ' is-invalid'
if instance.error_message.kind_of?(Array)
html = %(#{e}<div class="invalid-feedback">#{instance.error_message.uniq.join(', ')}</div>).html_safe
else
html = %(#{e}<div class="invalid-feedback">#{instance.error_message}</div>).html_safe
end
end
end
html
end
@ro31337

This comment has been minimized.

Copy link

ro31337 commented Sep 10, 2017

You better h(instance.error_message) and h(instance.error_message.uniq.join(', ')). If attacker has control over your error messages, you're screwed! Would be also nice to see example html/erb. It didn't work with my markup, error on line 17. Seems like I don't have class for my tag.

@plog

This comment has been minimized.

Copy link

plog commented Nov 9, 2017

undefined method `+' for nil:NilClass
with e['class'] += ' is-invalid'

@martinvelez

This comment has been minimized.

Copy link

martinvelez commented Nov 14, 2017

I achieved this using Javascript.

<script>                                                                            
$(document).ready(function(){                                                       
  $('.field_with_errors input:first').addClass('is-invalid')                        
  $('.field_with_errors select:first').addClass('is-invalid')                       
  $('.field_with_errors textarea:first').addClass('is-invalid')                     
});                                                                                 
</script> 
@andreimoment

This comment has been minimized.

Copy link

andreimoment commented Jan 11, 2018

Here's a version with the nil issue fixed: https://gist.github.com/andreimoment/515715d9d56ffbdadc25697d9db52c62

@ro31337, html_escape and its h alias seem to be unavailable for initializers. Users having control over error messages is not a common use case so I'm leaving this alone.

@pastullo

This comment has been minimized.

Copy link

pastullo commented Jun 7, 2018

How about this simpler solution which is compatible with Bootstrap 4:

# add in config/initializers/stock_errors.rb
ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
  html_tag.html_safe
end
@ycherniavskyi

This comment has been minimized.

Copy link

ycherniavskyi commented Jun 12, 2018

@pastullo how exactly your solution is compatible with Bootstrap 4? You just do not wrap html_tag by div with class field_with_errors, but also you do not add any necessary Bootstrap 4 from classes to highlight errors.

@rendon

This comment has been minimized.

Copy link

rendon commented Dec 17, 2018

My approach: add your error class to the element's classes instead of wrapping it in a div.

ActionView::Base.field_error_proc = proc do |html_tag, instance_tag|
  match = html_tag.to_s.match(/class\s*=\s*"([^"]*)"/)
  return html_tag unless match[1]

  html_tag
    .to_s
    .gsub(match[0], "class=\"#{match[1]} field_with_errors\"")
    .html_safe
end

I'm not sure how good/secure it is. It's just the first solution that I came up with.

I think you will have to update the regex to handle classes with single quotes like class='my-class'.

@rowend

This comment has been minimized.

Copy link

rowend commented Feb 1, 2020

This could be used as base

ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
  if html_tag =~ /class="(.*?)"/
    (html_tag.sub /class="(.*?)"/, 'class="\1 form-error"').html_safe
  else
    (html_tag.sub /(\/>|>)/, 'class="form-error" \1').html_safe
  end
end
@haroldus-

This comment has been minimized.

Copy link

haroldus- commented May 14, 2020

here is my solution

# /config/application.rb

    config.action_view.field_error_proc = Proc.new do |html_tag, instance|
      if html_tag.gsub!("class=\"", "class=\"field_with_errors ")
        %Q(#{html_tag}).html_safe
      elsif html_tag.gsub!("class='", "class='field_with_errors ")
        %Q(#{html_tag}).html_safe
      elsif html_tag.gsub!("<input", "<input class=\"field_with_errors\"")
        %Q(#{html_tag}).html_safe
      elsif html_tag.gsub!("<select", "<select class=\"field_with_errors\"")
        %Q(#{html_tag}).html_safe
      elsif html_tag.gsub!("<textarea", "<textarea class=\"field_with_errors\"")
        %Q(#{html_tag}).html_safe
      else
        %Q(<div class="field_with_errors">#{html_tag}</div>).html_safe
      end
    end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.