在父模型的验证错误之后,如何显示嵌套的表单验证错误? [英] How to show nested form validation errors after the validation errors for the parent model?

查看:49
本文介绍了在父模型的验证错误之后,如何显示嵌套的表单验证错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用Ruby on Rails 4.2,我有一个嵌套表单.在测试整个表单的验证时,我注意到嵌套表单的验证错误出现在验证错误列表的顶部,主表单的验证错误出现在下面.

Using Ruby on Rails 4.2, I have a nested form. When testing the validations for the entire form, I noticed that the validation errors for the nested form appear at the top of the validation errors list, with the validation errors for the main form appearing below.

这与声明它们的顺序相反(因为fields_for必须出现在父form_for的范围内),所以看起来像这样:

This is the opposite order that they are declared (since the fields_for has to appear within the scope of the parent form_for), so it looks like this:

[name        ]
[description ]
[others      ]
[nested #1   ]
[nested #2   ]

但是验证错误显示如下(使用空白作为示例验证错误):

But the validation errors appear like this (using blank as an example validation error):

  • 嵌套#1的NestedModelName不能为空.
  • 嵌套#2的NestedModelName不能为空.
  • 名称不能为空.
  • 说明不能为空.
  • 其他人不能为空.

这会使用户感到困惑,因为错误的显示顺序与错误在页面上的显示顺序无关.因为它显然只是依次验证每个模型,所以它并不希望根据它在表单中出现的位置将其放置在正确的位置,但是由于嵌套的表单模型通常是从​​属的,因此至少应将其添加到表单中.结束而不是在一开始就出现.有什么方法可以使嵌套的表单验证错误显示在父表单验证错误之后?

This is confusing for the user, since the errors appear out of order to how they appear on the page. It don't expect it to be in the correct position according to where it appears in the form, since it is obviously just validating each model in turn, but since the nested form model is usually subordinate, it should at least be added to the end instead of showing up in the beginning. Is there any way to get the nested form validation errors to appear after the parent form validation errors?

其他信息:

使用以下命令在视图中显示错误:

Errors are being displayed in the view using the 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">&times;</button>
      <p>#{sentence}</p>
      <ul>
        #{messages}
      </ul>
    </div>
    HTML

  end

,并在每个包含表单的视图文件中使用它:

and by using this in each view file containing a form:

<%= error_messages(@model) %>

推荐答案

更新1 :

我发现,如果您不需要担心i18n和应用程序文本的翻译,februaryInk的答案将非常接近正确.如果将所有验证都放在has_many :child_model 下方,则验证将以正确的顺序显示.但是,full_messages似乎没有使用语言环境文件转换模型或属性名称,因此,如果您需要翻译错误消息(我愿意),我的回答似乎仍然是一个不错的解决方案.

I discovered that the answer by februaryInk was very close to correct if you don't need to worry about i18n and translation of your application text. If you put the has_many :child_model underneath all your validations, the validations will appear in the correct order. However, full_messages doesn't appear to translate model or attribute names using the locale files, so if you require the error messages to be translated (which I do), my answer still seems like a decent solution.

更新2:

发布第一个更新后才意识到,通过删除更新1中使用发现进行排序的部分,我可以简化很多生成messages列表的代码,而只需保留进行翻译的部分即可.这是我的新解决方案,它是我的更新程序1和我的原始解决方案的结合.对于此更新的解决方案,有关config/locales/xx.ymlconfig/application.rb文件的所有其他信息仍然与原始解决方案相同.

Just realized after posting the first update that I could simplify my code that generates the messages list a lot by removing the part that does the ordering using the discovery in update 1, and just keep the part that does the translation. So here is my new solution, which is a combination of my update 1 and my original solution. All other information about the config/locales/xx.yml and config/application.rb files is still the same for this updated solution as it was for the 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

更新结束

我对application_helper.rb中的error_messages函数进行了一些更改,现在一切都按我想要的方式工作:主要表单验证错误在顶部,嵌套表单验证错误在下面,错误的顺序除了将嵌套的表单错误移到主表单错误下之外,不会更改.

I made a few changes to my error_messages function in application_helper.rb and now have everything working the way I wanted: main form validation errors are on the top, nested form validation errors are under those, the order of the errors does not change except moving the nested form errors under the main form errors.

我的解决方案是如下所示更改error_messages中的messages =行,并添加一个私有帮助器方法. (为了简化阅读和理解,可能应该将其分解为几个部分,但是我在控制台中进行了构建,以获取所需的内容,然后直接将其粘贴到那里.)

My solution was to change the messages = line in error_messages as shown below and to add a private helper method. (This should probably be broken down into parts to make it easier to read and understand, but I built it up in the console to get what I wanted and just pasted it directly from 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

由于我使用I18n来获取所有名称,因此该解决方案也应适用于其他其他语言环境.您还必须添加以下内容:

This solution should also work for other other locales, since I used I18n to get all the names. You will have to add the following also:

config/locales/en.yml

en:
  space: " "

因此,可以在单词之间有空格或没有空格的语言中正确处理模型和属性名称(我需要支持的第一个语言环境是中文,单词之间没有空格).例如,如果您确实需要支持中文,则可以使用以下方法:

This is so the model and attribute names will be handled correctly in languages that either have or don't have spaces between words (the first locale I need to support is Chinese, which doesn't have spaces between the words). If you did need to support Chinese, for example, you would use this:

config/locales/zh.yml

zh:
  space: ""

如果不必支持这种情况,则可以将所有I18n.t('space')实例替换为" ".模型和属性名称也可以翻译为,但是同样,如果您不需要英语以外的语言环境,则无需执行任何操作(尽管您可以使用en.yml文件来更改模型名称或显示的属性).

If you don't have to support this case, all instances of I18n.t('space') can be replaced with " ". The model and attribute names can also be translated as, but again if you don't need to support locales beyond English you don't need to do anything (although you can use the en.yml file to change the names of the model or attributes that are displayed).

作为示例,使用en.yml更改使用常见的Authors/Books示例显示的名称:

As an example using en.yml to change the names displayed using the 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"

在此示例中,如果未在en.yml中添加上述内容,则默认值为:

In this example the default, without the above additions to en.yml, would be:

  • 名称不能为空.
  • 书名不能为空.
  • 出版的书不能空白.

但是加上en.yml的上述补充,它将是:

But with the above additions to en.yml it would be:

  • nom de plume不能为空.
  • 稿件标题不能为空.
  • 原稿年份不能为空.

当然,如果您有一个带有适当翻译的zh.yml文件,那么其中的内容都会显示出来.

And of course, if you have a zh.yml file with the appropriate translations, whatever you have in those would show up instead.

如果您确实需要支持多个语言环境,请不要忘记在config/application.rb中添加以下内容(此部分仅经过表面测试,可能需要一些其他配置):

If you do need to support multiple locales, don't forget to add the following to config/application.rb (this part was only tested superficially, and may need some additional configuration):

config/application.rb

config.i18n.available_locales = [:zh, :en]
config.i18n.default_locale = :en

这篇关于在父模型的验证错误之后,如何显示嵌套的表单验证错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆