ruby on rails - How to show nested form validation errors after the validation errors for the parent model? -
using ruby on rails 4.2, have nested form. when testing validations entire form, noticed validation errors nested form appear @ top of validation errors list, validation errors main form appearing below.
this opposite order declared (since fields_for
has appear within scope of parent form_for
), looks this:
[name ] [description ] [others ] [nested #1 ] [nested #2 ]
but validation errors appear (using blank example validation error):
- nestedmodelname nested #1 can't blank.
- nestedmodelname nested #2 can't blank.
- name can't blank.
- description can't blank.
- others can't blank.
this confusing user, since errors appear out of order how appear on page. don't expect in correct position according appears in form, since validating each model in turn, since nested form model subordinate, should @ least added end instead of showing in beginning. there way nested form validation errors appear after parent form validation errors?
additional info:
errors being displayed in view using following:
application_helper.rb
def error_messages(resource) return '' if resource.errors.empty? messages = resource.errors.full_messages.map { |msg| content_tag(:li, msg) }.join sentence = i18n.t('errors.messages.not_saved', count: resource.errors.count, resource: resource.class.model_name.human.downcase) html = <<-html <div class="validation-error alert alert-danger alert-dismissable fade in alert-block"> <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button> <p>#{sentence}</p> <ul> #{messages} </ul> </div> html end
and using in each view file containing form:
<%= error_messages(@model) %>
update 1:
i discovered answer februaryink close correct if don't need worry i18n , translation of application text. if put has_many :child_model
underneath validations, validations appear in correct order. however, full_messages
doesn't appear translate model or attribute names using locale files, if require error messages translated (which do), answer still seems decent solution.
update 2:
just realized after posting first update simplify code generates messages
list lot removing part ordering using discovery in update 1, , keep part translation. here new solution, combination of update 1 , original solution. other information config/locales/xx.yml
, config/application.rb
files still same updated solution original.
app/models/parent_model.rb
... validates :name, # validations hash validates :description, # validations hash validates :others, # validations hash has_many :child_models accepts_nested_attributes_for :child_models ...
app/models/child_model.rb
... validates :nested_1, # validations hash validates :nested_2, # validations hash ...
app/helpers/application_helper.rb
messages = resource.errors.messages.keys.map {|value| error_message_attribute(resource, value) + i18n.t('space') + resource.errors.messages[value].first}.map { |msg| content_tag(:li, msg) }.join private def error_message_attribute(resource, symbol) if symbol.to_s.split(".").length > 1 model_name, attribute_name = symbol.to_s.split(".") model_class = model_name.singularize.camelize.constantize model_class.model_name.human + i18n.t('space') + model_class.human_attribute_name(attribute_name).downcase else resource.class.human_attribute_name(symbol) end end
end of update
i made few changes error_messages
function in application_helper.rb
, have working way wanted: main form validation errors on top, nested form validation errors under those, order of errors not change except moving nested form errors under main form errors.
my solution change messages =
line in error_messages
shown below , add private helper method. (this should broken down parts make easier read , understand, built in console wanted , pasted directly there).
app/helpers/application_helper.rb
messages = hash[resource.errors.messages.keys.map.with_index(1) { |attribute, index| [attribute, [index, attribute.match(/\./) ? 1 : 0]] }].sort_by {|attribute, data| [data[1], data[0]]}.collect { |attributes| attributes[0]}.map {|value| error_message_attribute_name(resource, value) + i18n.t('space') + resource.errors.messages[value].first}.map { |msg| content_tag(:li, msg) }.join private def error_message_attribute_name(resource, symbol) if symbol.to_s.split(".").length > 1 model_name, attribute_name = symbol.to_s.split(".") model_class = model_name.singularize.camelize.constantize model_class.model_name.human + i18n.t('space') + model_class.human_attribute_name(attribute_name).downcase else resource.class.human_attribute_name(symbol) end end
this solution should work other other locales, since used i18n
names. have add following also:
config/locales/en.yml
en: space: " "
this model , attribute names handled correctly in languages either have or don't have spaces between words (the first locale need support chinese, doesn't have spaces between words). if did need support chinese, example, use this:
config/locales/zh.yml
zh: space: ""
if don't have support case, instances of i18n.t('space')
can replaced " "
. model , attribute names can translated as, again if don't need support locales beyond english don't need (although can use en.yml
file change names of model or attributes displayed).
as example using en.yml
change names displayed using common authors/books example:
config/locales/en.yml
en: activerecord: models: author: "writer" book: "manuscript" attributes: author: name: "non de plume" book: name: "title" published: "year"
in example default, without above additions en.yml
, be:
- name can't blank.
- book name can't blank.
- book published can't blank.
but above additions en.yml
be:
- nom de plume can't blank.
- manuscript title can't blank.
- manuscript year can't blank.
and of course, if have zh.yml
file appropriate translations, whatever have in show instead.
if need support multiple locales, don't forget add following config/application.rb
(this part tested superficially, , may need additional configuration):
config/application.rb
config.i18n.available_locales = [:zh, :en] config.i18n.default_locale = :en
Comments
Post a Comment