Start to abstract validation message rendering by declare a descendant
to the ActionView::Helpers::FormBuilder
class named
ValidationMessageFormBuilder
. Set it as the application’s default by
calling default_form_builder
from the ApplicationController
class.
First and foremost, declare the #errors
method, which retrieves
validation messages for an attribute. When passed a block, yield the
collection of ActiveModel::Errors
. When no block is passed, return the
collection of ActiveModel::Errors
.
Next, ensure that calls to form field builders set
[aria-invalid="true"]
when rendering a field with error associated to
the attribute. When rendering checked
-capable fields like <input
type="checkbox">
and <input type="radio">
, only render
[aria-invalid="true"]
on the element that is also checked
.
Collapse app/controllers/application_controller.rb
Expand app/controllers/application_controller.rb
app/controllers/application_controller.rb
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 9ebbc14..d2e3413 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -1,5 +1,9 @@
+require "./lib/validation_message_form_builder"
+
class ApplicationController < ActionController::Base
include FlashParams
before_action { Current.user = Authentication.find(session) }
+
+ default_form_builder ValidationMessageFormBuilder
end
Collapse app/views/authentications/new.html.erb
Expand app/views/authentications/new.html.erb
app/views/authentications/new.html.erb
diff --git a/app/views/authentications/new.html.erb b/app/views/authentications/new.html.erb
index fb2f95a..7972cb5 100644
--- a/app/views/authentications/new.html.erb
+++ b/app/views/authentications/new.html.erb
@@ -1,26 +1,24 @@
<%= form_with(model: authentication) do |form| %>
- <% form.object.errors[:base].tap do |errors| %>
+ <% form.errors(:base) do |errors| %>
<% if errors.any? %>
<%= tag.span errors.to_sentence, aria: { live: "assertive" } %>
<% end %>
<% end %>
- <% form.object.errors[:username].tap do |errors| %>
+ <% form.errors(:username) do |errors| %>
<%= form.label :username %>
<%= form.text_field :username, aria: {
describedby: {form.field_id(:username, :validation_message) => errors.any?},
- invalid: errors.any?
} %>
<% if errors.any? %>
<%= tag.span errors.to_sentence, id: form.field_id(:username, :validation_message) %>
<% end %>
<% end %>
- <% form.object.errors[:password].tap do |errors| %>
+ <% form.errors(:password) do |errors| %>
<%= form.label :password %>
<%= form.password_field :password, aria: {
describedby: {form.field_id(:password, :validation_message) => errors.any?},
- invalid: errors.any?
} %>
<% if errors.any? %>
<%= tag.span errors.to_sentence, id: form.field_id(:password, :validation_message) %>
Collapse app/views/messages/index.html.erb
Expand app/views/messages/index.html.erb
app/views/messages/index.html.erb
diff --git a/app/views/messages/index.html.erb b/app/views/messages/index.html.erb
index 49bfab9..5b072a8 100644
--- a/app/views/messages/index.html.erb
+++ b/app/views/messages/index.html.erb
@@ -6,11 +6,10 @@
<% if Current.user %>
<%= form_with model: message do |form| %>
- <% form.object.errors[:content].tap do |errors| %>
+ <% form.errors(:content) do |errors| %>
<%= form.label :content %>
<%= form.rich_text_area :content, aria: {
describedby: {form.field_id(:content, :validation_message) => errors.any?},
- invalid: errors.any?
} %>
<% if errors.any? %>
<%= tag.span errors.to_sentence, id: form.field_id(:content, :validation_message) %>
Collapse config/initializers/rails.rb
Expand config/initializers/rails.rb
config/initializers/rails.rb
diff --git a/config/initializers/rails.rb b/config/initializers/rails.rb
new file mode 100644
index 0000000..79526f7
--- /dev/null
+++ b/config/initializers/rails.rb
@@ -0,0 +1 @@
+Dir[Rails.root.join("lib", "rails_ext", "*")].each { |path| require "rails_ext/#{File.basename(path)}" }
Collapse lib/rails_ext/action_view.rb
Expand lib/rails_ext/action_view.rb
lib/rails_ext/action_view.rb
diff --git a/lib/rails_ext/action_view.rb b/lib/rails_ext/action_view.rb
new file mode 100644
index 0000000..b09d171
--- /dev/null
+++ b/lib/rails_ext/action_view.rb
@@ -0,0 +1,47 @@
+module AriaTagsExtension
+ def render
+ if object.respond_to?(:errors) && object.errors[@method_name].any?
+ @options["aria-invalid"] ||= "true"
+ end
+
+ super
+ end
+end
+
+module CheckableAriaTagsExtension
+ def render
+ if object.respond_to?(:errors) && object.errors[@method_name].any?
+ @options["aria-invalid"] ||= ("true" if @options["checked"])
+ end
+
+ super
+ end
+end
+
+ActiveSupport.on_load :action_view do
+ module ActionView
+ module Helpers
+ module Tags
+ class ActionText
+ prepend AriaTagsExtension
+ end
+
+ class Select
+ prepend AriaTagsExtension
+ end
+
+ class TextField
+ prepend AriaTagsExtension
+ end
+
+ class TextArea
+ prepend AriaTagsExtension
+ end
+
+ [RadioButton, CheckBox].each do |kls|
+ prepend CheckableAriaTagsExtension
+ end
+ end
+ end
+ end
+end
Collapse lib/validation_message_form_builder.rb
Expand lib/validation_message_form_builder.rb
lib/validation_message_form_builder.rb
diff --git a/lib/validation_message_form_builder.rb b/lib/validation_message_form_builder.rb
new file mode 100644
index 0000000..4851981
--- /dev/null
+++ b/lib/validation_message_form_builder.rb
@@ -0,0 +1,7 @@
+class ValidationMessageFormBuilder < ActionView::Helpers::FormBuilder
+ def errors(field, &block)
+ validation_messages = object.respond_to?(:errors) ? object.errors[field] : []
+
+ block ? yield(validation_messages) : validation_messages
+ end
+end