Resources

Download Source Code

Summary

# application.css
.field_with_errors {
  color: red;
}

.field_with_errors input {
  border-color: red;
}

# _form.html.erb
<%= form_with(model: user, local: true) do |form| %>
  <% if user.errors.any? %>
    <div class='alert alert-danger'>There was a problem with saving this form.</div>
  <% end %>

  <div class="form-group">
    <%= form.label :first_name %>
    <%= form.text_field :first_name, class: 'form-control' %>
    <%= form.errors :first_name %>
  </div>

  <div class="form-group">
    <%= form.label :last_name %>
    <%= form.text_field :last_name, class: 'form-control' %>
    <%= form.errors :last_name %>
  </div>

  <div class="form-group">
    <%= form.label :age %>
    <%= form.number_field :age, class: 'form-control' %>
    <%= form.errors :age %>
  </div>

  <div class="actions">
    <%= form.submit class: 'btn btn-primary' %>
  </div>
<% end %>

# config/initializers/core_ext/form_builder/errors.rb
class ActionView::Helpers::FormBuilder
  def errors(field_name)
    if self.object.errors[field_name].present?
      model_name = self.object.class.name.downcase
      id = "error_#{model_name}_#{field_name}"
      parent_id = "#{model_name}_#{field_name}"
      string = %{
        <label id=#{id} for="#{parent_id}" class="field_with_errors">
          #{self.object.errors[field_name].join(', ')}
        </label>
      }
      string.squish.html_safe
    end
  end
end

# models/user.rb
class User < ApplicationRecord
  validates :first_name, presence: true
  validates :last_name, length: { minimum: 2, maximum: 32 },
                        allow_blank: true
  validates :age, inclusion: { in: 18..99, message: "must be 18+" },
                  length: { maximum: 2 }
end