Skip to content

Instantly share code, notes, and snippets.

@webuilder240
Last active March 30, 2025 02:34
Show Gist options
  • Select an option

  • Save webuilder240/cd497cb2bfceb5ff19b1019e906e074b to your computer and use it in GitHub Desktop.

Select an option

Save webuilder240/cd497cb2bfceb5ff19b1019e906e074b to your computer and use it in GitHub Desktop.
class ValidationFormBuilder < ActionView::Helpers::FormBuilder
include ActionView::Helpers::TagHelper
include ActionView::Context # 必須
def validation_text_field(attribute, options = {})
field_with_feedback(:text_field, attribute, options)
end
def validation_email_field(attribute, options = {})
field_with_feedback(:email_field, attribute, options)
end
def validation_number_field(attribute, options = {})
field_with_feedback(:number_field, attribute, options)
end
def validation_password_field(attribute, options = {})
field_with_feedback(:password_field, attribute, options)
end
def validation_text_area(attribute, options = {})
field_with_feedback(:text_area, attribute, options)
end
private
def field_with_feedback(method, attribute, options)
input_html = send("validatable_#{method}", attribute, options)
feedback_html = error_feedback(attribute)
safe_join([ input_html, feedback_html ])
end
def error_feedback(attribute)
errors = object.errors[attribute]
model_name = object_name # ← Rails FormBuilder が自動でセットするモデル名
# 属性名を翻訳
attribute_name = object.class.human_attribute_name(attribute)
content_tag(:div, id: "#{model_name}_#{attribute}-feedback") do
if errors.present?
# エラーメッセージに属性名を追加
formatted_errors = errors.map do |error|
# エラーメッセージが既に属性名を含んでいる場合はそのまま使用
if error.include?(attribute_name)
error
else
"#{attribute_name}#{error}"
end
end
content_tag(:span, formatted_errors.join(", "), class: "error")
else
"".html_safe
end
end
end
def validatable_text_field(attribute, options)
options = add_validation_attributes(attribute, options)
text_field(attribute, options)
end
# validatable_XXX のバリデーション属性を追加する処理(既に作っていたヘルパー相当)
def validatable_email_field(attribute, options)
options = add_validation_attributes(attribute, options)
email_field(attribute, options)
end
def validatable_number_field(attribute, options)
options = add_validation_attributes(attribute, options)
number_field(attribute, options)
end
def validatable_password_field(attribute, options)
options = add_validation_attributes(attribute, options)
password_field(attribute, options)
end
def validatable_text_area(attribute, options)
options = add_validation_attributes(attribute, options)
text_area(attribute, options)
end
def add_validation_attributes(attribute, options)
validators = object.class.validators_on(attribute)
html = options.deep_dup
model_name = object.class.model_name.human
# 属性名を翻訳
attribute_name = object.class.human_attribute_name(attribute)
if validators.any? { |v| v.is_a?(ActiveModel::Validations::PresenceValidator) }
html[:required] = true
# エラーメッセージを手動で生成
message = I18n.t("errors.messages.blank")
html[:"data-message-required"] ||= "#{attribute_name}#{message}"
end
if validator = validators.find { |v| v.is_a?(ActiveModel::Validations::LengthValidator) }
if validator.options[:minimum]
html[:minlength] = validator.options[:minimum]
# エラーメッセージを手動で生成
message = I18n.t("errors.messages.too_short", count: validator.options[:minimum])
html[:"data-message-too-short"] ||= "#{attribute_name}#{message}"
end
end
if validator = validators.find { |v| v.is_a?(ActiveModel::Validations::NumericalityValidator) }
if min = validator.options[:greater_than_or_equal_to]
html[:min] = min
# エラーメッセージを手動で生成
message = I18n.t("errors.messages.greater_than_or_equal_to", count: min)
html[:"data-message-range-underflow"] ||= "#{attribute_name}#{message}"
end
if max = validator.options[:less_than_or_equal_to]
html[:max] = max
# エラーメッセージを手動で生成
message = I18n.t("errors.messages.less_than_or_equal_to", count: max)
html[:"data-message-range-overflow"] ||= "#{attribute_name}#{message}"
end
end
html
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment