问题
I found the following code at How do I validate members of an array field? .
# Validates the values of an Enumerable with other validators.
# Generates error messages that include the index and value of
# invalid elements.
#
# Example:
#
# validates :values, enum: { presence: true, inclusion: { in: %w{ big small } } }
#
class EnumValidator < ActiveModel::EachValidator
def initialize(options)
super
@validators = options.map do |(key, args)|
create_validator(key, args)
end
end
def validate_each(record, attribute, values)
helper = Helper.new(@validators, record, attribute)
Array.wrap(values).each do |value|
helper.validate(value)
end
end
private
class Helper
def initialize(validators, record, attribute)
@validators = validators
@record = record
@attribute = attribute
@count = -1
end
def validate(value)
@count += 1
@validators.each do |validator|
next if value.nil? && validator.options[:allow_nil]
next if value.blank? && validator.options[:allow_blank]
validate_with(validator, value)
end
end
def validate_with(validator, value)
before_errors = error_count
run_validator(validator, value)
if error_count > before_errors
prefix = "element #{@count} (#{value}) "
(before_errors...error_count).each do |pos|
error_messages[pos] = prefix + error_messages[pos]
end
end
end
def run_validator(validator, value)
validator.validate_each(@record, @attribute, value)
rescue NotImplementedError
validator.validate(@record)
end
def error_messages
@record.errors.messages[@attribute]
end
def error_count
error_messages ? error_messages.length : 0
end
end
def create_validator(key, args)
opts = {attributes: attributes}
opts.merge!(args) if args.kind_of?(Hash)
validator_class(key).new(opts).tap do |validator|
validator.check_validity!
end
end
def validator_class(key)
validator_class_name = "#{key.to_s.camelize}Validator"
validator_class_name.constantize
rescue NameError
"ActiveModel::Validations::#{validator_class_name}".constantize
end
end
It is the only way that works i have found of validating a rails form input that is expecting an array. The problem is that the error message for invalid entries is not in a clean flash message but the typical rails error page:
For example, my form has a multiple select input field in which the user can enter several tags from a list. If the user enters tags that are not from the list, i want the validation to occur and for the user to be told that they must pick an item from the list. How do I change the error message into a clean flash message?
回答1:
It appears that the validator is raising an error instead of simply adding error messages to the errors stack. I'd recommend wrapping the update_attributes block in abegin/rescue/end` block.
For changing the message, you can take two routes:
You could customize the
EnumValidatorto use keys from your locale file so that each entry might say something like the "element %{count} is not in the list". You should be able to customizeactiverecord.errors.messages.inclusionfor the "is not in the list" portion of the message. For the "element 0 (rails)" portion, you could either hard-code the phrase, as is currently implemented (not recommended) or make use of the locale file again by adding a key for say,enum_element, then in your EnumValidator, callI18n.translate(:'activerecord.errors.messages.enum_element', count: @count, value: value).After validation, check
errors.keysfor for issues on your enumerable column. If you find errors, remove those keys and add your own message for that key.
As for your form, it sounds like you're using a dynamic form builder. If it's SimpleForm, you could try specifying the exact type the input should be with the as: :select option.
来源:https://stackoverflow.com/questions/19913373/change-validation-error-from-active-record-error-view-page-to-clean-flash-messag