Last active
October 2, 2023 23:19
-
-
Save ahmad19/9092275c6c87a8ac6699df9362b06023 to your computer and use it in GitHub Desktop.
Render inline errors in Rails forms after submit and hotwire changes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%= form_for customer, html: { id: dom_id(customer), class: 'row g-3' } do |f| %> | |
<% presenter = InlineErrorRenderer.new(customer, self, 'form-control-lg form-control-solid mb-3 mb-lg-0') %> | |
<div class='col-md-6'> | |
<%= f.label :full_name %> | |
<%= f.text_field :full_name, presenter.html_options_for(:full_name) %> | |
<%= presenter.error_container_for(:full_name) %> | |
</div> | |
<div class='col-md-6'> | |
<%= f.label :phone_number, 'Phone Number' %> | |
<%= f.text_field :phone_number, presenter.html_options_for(:phone_number) %> | |
<%= presenter.error_container_for(:phone_number) %> | |
</div> | |
<div class='col-md-6'> | |
<%= f.label :email, 'Email' %> | |
<%= f.text_field :email, presenter.html_options_for(:email) %> | |
<%= presenter.error_container_for(:email) %> | |
</div> | |
<div class="col-md-6"> | |
<%= f.label :country, 'Country' %> | |
<%= country_select(f, "country", { priority_countries: ['IN', 'US'], selected: "IN" }, presenter.html_options_for(:country)) %> | |
<%= presenter.error_container_for(:country) %> | |
</div> | |
<div class="col-12"> | |
<%= f.submit 'Submit', class: 'btn btn-outline-secondary' %> | |
<%= link_to 'Back', customers_path, class: 'btn btn-outline-warning' %> | |
</div> | |
<% end %> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Rails forms when in error state wraps each form element inside div with "field_with_errors" class. | |
// Therefore bootstrap error classes (invalid-feedback, is-invalid) won't work because of following structure. | |
// <div class="form-control"> | |
// <div class="field_with_errors"> | |
// <input type="text" class="is-invalid" | |
// </div> | |
// </div> | |
.invalid-feedback { | |
display: block !important; | |
} | |
.form-control.form-control-solid.is-invalid { | |
border-color: #F1416C; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This is optional but if you want to use hotwire then just return the error response in turbo_stream in controller | |
def create | |
@customer.user_id = current_user.id | |
respond_to do |format| | |
if @customer.save | |
# do usual stuff | |
else | |
format.turbo_stream | |
end | |
end | |
end | |
# and handle that turbo_stream response in create.turbo_stream.erb | |
<%= turbo_stream.replace "new_customer", partial: "customers/form", locals: { customer: @customer } %> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# InlineErrorRenderer is based on TwitterBootstrap's server side form validations. | |
# https://getbootstrap.com/docs/5.0/forms/validation/#server-side | |
class InlineErrorRenderer | |
def initialize(model, template, default_classes) | |
@model = model | |
@template = template | |
@default_classes = default_classes | |
end | |
FORM_INVALID = '%s %s is-invalid' | |
ARIA = '%s_aria' | |
def html_options_for(attr, html_class = nil) | |
if errors[attr].present? | |
{ | |
class: format(FORM_INVALID, default_classes, html_class), | |
aria: { describedby: format(ARIA, attr) } | |
} | |
else | |
{ class: default_classes } | |
end | |
end | |
def error_container_for(attr) | |
if errors[attr].present? | |
template.tag.div(class: 'invalid-feedback', id: format(ARIA, attr)) do | |
errors[attr].join(',').html_safe | |
end | |
end | |
end | |
private | |
attr_reader :model, :template, :default_classes | |
def errors | |
@errors ||= model.errors | |
end | |
end |
With hotwire added, it stays on the same page customers/new
and renders the errored state of the form.
Screen.Recording.2021-06-06.at.11.20.52.AM.mov
This code doesn't work. I got an error:
"cannot load such file -- I:/RB/proj/AskIt/app/controllers/InlineErrorRenderer.rb"
This code doesn't work. I got an error: "cannot load such file -- I:/RB/proj/AskIt/app/controllers/InlineErrorRenderer.rb"
@vasilevskykv class InlineErrorRenderer
is meant to be a service class and not a controller. Add that file in a new folder inside app called as service and then create a file with the name inline_error_renderer.rb
and then paste that code. It should work then.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is how it will be displayed after the form submit