如何在父模型的验证错误之后显示嵌套的表单验证错误?

时间:2022-10-24 14:58:39

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.

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

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:

这是声明它们的相反顺序(因为fields_for必须出现在父form_for的范围内),因此看起来如下:

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

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

但验证错误如下所示(以空白为例验证错误):

  • NestedModelName nested #1 can't be blank.
  • NestedModelName嵌套#1不能是空的。
  • NestedModelName nested #2 can't be blank.
  • 嵌套#2的NestedModelName不能为空。
  • Name can't be blank.
  • 名字不能空白。
  • Description can't be blank.
  • 描述不能空白。
  • Others can't be blank.
  • 别人不能空白。

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?

这让用户感到困惑,因为错误的出现顺序与它们在页面上的显示顺序不一致。不要指望它在正确的位置根据它出现在表单,因为它显然只是验证每一个模型,但由于嵌套形式模型通常是下属,它至少应该被添加到结束,而不是开始出现。是否有办法在父表单验证错误之后显示嵌套的表单验证错误?

Additional Info:

额外的信息:

Errors are being displayed in the view using the following:

在视图中显示的错误如下:

application_helper.rb

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) %>

2 个解决方案

#1


2  

The order for error messages seems to reflect the order of the validations and accepts_nested_attributes_for in your model file. Put the validations in the order you want them to come, with accepts_nested_attributes_for last. To get the order you gave as an example, try this:

错误消息的顺序似乎反映了模型文件中验证和accepts_nested_attributes_for的顺序。将验证按照希望的顺序进行,最后使用accepts_nested_attributes_for。为了得到你举的例子,试试这个:

parent_model.rb

parent_model.rb

...

validates :name, # validations hash
validates :description, # validations hash
validates :others, # validations hash

accepts_nested_attributes_for :child_model

...

child_model.rb

child_model.rb

...

validates :nested_1, # validations hash
validates :nested_2, # validations hash

...

The order of the individual validations in each hash also seems to have an effect in that it changes the order that a particular attribute's error messages are displayed.

每个散列中每个验证的顺序似乎也有影响,因为它改变了显示特定属性的错误消息的顺序。

#2


2  

Update 1:

更新1:

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.

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

Update 2:

更新2:

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.

在发布了第一个更新之后,我才意识到我可以简化我的代码,通过删除在更新1中使用发现的部分来生成消息列表,并且只保留做翻译的部分。这是我的新解决方案,它是我的update 1和原始解决方案的结合。关于配置/地区/xx的所有其他信息。yml和配置应用程序。对于这个更新的解决方案,rb文件仍然与原来的一样。

app/models/parent_model.rb

应用程序/模型/ 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

应用程序/模型/ child_model.rb

...

validates :nested_1, # validations hash
validates :nested_2, # validations hash

...

app/helpers/application_helper.rb

应用程序引入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 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.

我对application_helper中的error_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).

我的解决方案是更改error_messages中的message = line,并添加一个private helper方法。(为了便于阅读和理解,应该将它分解成不同的部分,但是我在控制台中构建了它,以获得我想要的内容,并直接从那里粘贴)。

app/helpers/application_helper.rb

应用程序引入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 also work for other other locales, since I used I18n to get all the names. You will have to add the following also:

这个解决方案也适用于其他地区,因为我使用I18n获取所有的名称。您还需要添加以下内容:

config/locales/en.yml

配置/地区/ 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:

这样,模型和属性名称将被正确地处理在语言中,或者在单词之间没有空格(我需要支持的第一个locale是中文,在单词之间没有空格)。例如,如果你确实需要支持中文,你可以这样做:

config/locales/zh.yml

配置/地区/ zh.yml

zh:
  space: ""

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).

如果您不需要支持这种情况,那么I18n.t('space')的所有实例都可以替换为“”。模型和属性名称也可以被翻译为,但是如果您不需要支持英语以外的地区,您就不需要做任何事情(尽管您可以使用en)。yml文件更改显示的模型或属性的名称)。

As an example using en.yml to change the names displayed using the common Authors/Books example:

以en为例。使用常见作者/书籍示例更改所显示的名称:

config/locales/en.yml

配置/地区/ en.yml

en:
  activerecord:
    models:
      author: "writer"
      book: "manuscript"
    attributes:
      author:
        name: "non de plume"
      book:
        name: "title"
        published: "year"

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

在本例中,缺省情况下,没有上述添加到en。yml,将:

  • Name can't be blank.
  • 名字不能空白。
  • Book name can't be blank.
  • 书的名字不能是空的。
  • Book published can't be blank.
  • 出版的书不能是空白的。

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

但是加上上面的en。yml,那将是:

  • Nom de plume can't be blank.
  • 笔名不能为空。
  • Manuscript title can't be blank.
  • 手稿标题不能是空的。
  • Manuscript year can't be blank.
  • 手稿年不能是空白的。

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

当然,如果有zh。yml文件中有适当的翻译,无论你有什么,都会显示出来。

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):

如果您确实需要支持多个地区,请不要忘记向配置/应用程序添加以下内容。rb(这部分只做了表面测试,可能需要一些额外的配置):

config/application.rb

配置/ application.rb

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

#1


2  

The order for error messages seems to reflect the order of the validations and accepts_nested_attributes_for in your model file. Put the validations in the order you want them to come, with accepts_nested_attributes_for last. To get the order you gave as an example, try this:

错误消息的顺序似乎反映了模型文件中验证和accepts_nested_attributes_for的顺序。将验证按照希望的顺序进行,最后使用accepts_nested_attributes_for。为了得到你举的例子,试试这个:

parent_model.rb

parent_model.rb

...

validates :name, # validations hash
validates :description, # validations hash
validates :others, # validations hash

accepts_nested_attributes_for :child_model

...

child_model.rb

child_model.rb

...

validates :nested_1, # validations hash
validates :nested_2, # validations hash

...

The order of the individual validations in each hash also seems to have an effect in that it changes the order that a particular attribute's error messages are displayed.

每个散列中每个验证的顺序似乎也有影响,因为它改变了显示特定属性的错误消息的顺序。

#2


2  

Update 1:

更新1:

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.

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

Update 2:

更新2:

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.

在发布了第一个更新之后,我才意识到我可以简化我的代码,通过删除在更新1中使用发现的部分来生成消息列表,并且只保留做翻译的部分。这是我的新解决方案,它是我的update 1和原始解决方案的结合。关于配置/地区/xx的所有其他信息。yml和配置应用程序。对于这个更新的解决方案,rb文件仍然与原来的一样。

app/models/parent_model.rb

应用程序/模型/ 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

应用程序/模型/ child_model.rb

...

validates :nested_1, # validations hash
validates :nested_2, # validations hash

...

app/helpers/application_helper.rb

应用程序引入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 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.

我对application_helper中的error_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).

我的解决方案是更改error_messages中的message = line,并添加一个private helper方法。(为了便于阅读和理解,应该将它分解成不同的部分,但是我在控制台中构建了它,以获得我想要的内容,并直接从那里粘贴)。

app/helpers/application_helper.rb

应用程序引入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 also work for other other locales, since I used I18n to get all the names. You will have to add the following also:

这个解决方案也适用于其他地区,因为我使用I18n获取所有的名称。您还需要添加以下内容:

config/locales/en.yml

配置/地区/ 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:

这样,模型和属性名称将被正确地处理在语言中,或者在单词之间没有空格(我需要支持的第一个locale是中文,在单词之间没有空格)。例如,如果你确实需要支持中文,你可以这样做:

config/locales/zh.yml

配置/地区/ zh.yml

zh:
  space: ""

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).

如果您不需要支持这种情况,那么I18n.t('space')的所有实例都可以替换为“”。模型和属性名称也可以被翻译为,但是如果您不需要支持英语以外的地区,您就不需要做任何事情(尽管您可以使用en)。yml文件更改显示的模型或属性的名称)。

As an example using en.yml to change the names displayed using the common Authors/Books example:

以en为例。使用常见作者/书籍示例更改所显示的名称:

config/locales/en.yml

配置/地区/ en.yml

en:
  activerecord:
    models:
      author: "writer"
      book: "manuscript"
    attributes:
      author:
        name: "non de plume"
      book:
        name: "title"
        published: "year"

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

在本例中,缺省情况下,没有上述添加到en。yml,将:

  • Name can't be blank.
  • 名字不能空白。
  • Book name can't be blank.
  • 书的名字不能是空的。
  • Book published can't be blank.
  • 出版的书不能是空白的。

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

但是加上上面的en。yml,那将是:

  • Nom de plume can't be blank.
  • 笔名不能为空。
  • Manuscript title can't be blank.
  • 手稿标题不能是空的。
  • Manuscript year can't be blank.
  • 手稿年不能是空白的。

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

当然,如果有zh。yml文件中有适当的翻译,无论你有什么,都会显示出来。

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):

如果您确实需要支持多个地区,请不要忘记向配置/应用程序添加以下内容。rb(这部分只做了表面测试,可能需要一些额外的配置):

config/application.rb

配置/ application.rb

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