【问题标题】:How to use error_messages_for in a multi-model form?如何以多模型形式使用error_messages_for?
【发布时间】:2010-02-27 02:28:02
【问题描述】:

我有两个模型:专辑和曲目。 Album 有很多曲目,Track 属于专辑。

我希望能够在创建专辑时根据需要创建尽可能多的曲目,类似于 railscasts episode 197。不过,与 railscasts 剧集不同的是,Track 表单同时包含标题和描述 - 两者都是必需的。

现在,表单如下所示:

Create New Album

Name: [      ]

    Track (remove link)
          Name:        [      ]
          Description: [      ]

    Track (remove link)
          Name:        [      ]
          Description: [      ]

(add track link)

如果我决定提交空白表单,我会在表单顶部收到以下错误消息:

Description can't be blank
Title can't be blank
Title can't be blank

这些错误消息并非特定于模型,都位于页面顶部,并且每个模型只出现一次(请注意,我将 both 的字段和错误消息留空只出现一次 - 不特定于哪个轨道)。


为了生成初始曲目字段,我在专辑控制器的新操作中添加了以下行:2.times { @album.tracks.build }

我的表单的要点是这样的:

<% form_for @album do |f| %>
<%= f.error_messages %>

<%= f.label :title %><br />
<%= f.text_field :title %>

<% f.fields_for :tracks do |f, track| %>
  <%= render :partial => 'tracks/fields', :locals => {:f => f} %>
<% end %>

<%= f.submit "Submit" %>
<% end %>

我尝试用&lt;%= error_messages_for @album %&gt; 替换顶部的&lt;%= f.error_messages %&gt;(仅显示专辑的消息),并添加&lt;%= error_messages_for track %&gt;(显示特定于每个曲目的错误消息)-但这并没有诡计。 有人知道如何处理吗?

谢谢!

【问题讨论】:

    标签: ruby-on-rails multi-model-forms


    【解决方案1】:

    如果您想将父对象和子对象的错误消息分开,可能会有点复杂。因为当您保存父对象时,它也会验证子对象并且它包含子对象的错误。所以你可以这样做:

    <% form_for @album do |f| %>
    <%= custom_error_messages_helper(@album) %>
    
    <%= f.label :title %><br />
    <%= f.text_field :title %>
    
    <% f.fields_for :tracks do |t| %>
      <%= t.error_messages message => nil, :header_message => nil %>
      <%= render :partial => 'tracks/fields', :locals => {:f => t} %>
    <% end %>
    
    <%= f.submit "Submit" %>
    <% end %>
    

    或者您可以将这一行与t.error_messages 放在'tracks/fields' 部分中(我将表单构建器对象表单f 重命名为t,因为它令人困惑)。它应该只显示特定子对象的错误(至少它对我有用)(这样你就可以看到哪个对象的标题错误)。另外请记住,Rails 会自动将 css 类 fieldWithErrors 添加到包含错误的字段中,例如添加到 css:

    .fieldWithErrors {
      border: 1px solid red;
    }
    

    父对象的错误更复杂,因为@album.errors 也包含子对象的错误。我没有找到任何好的简单方法来删除一些错误或只显示与父对象关联的错误,所以我的想法是编写自定义帮助器来处理它:

    def custom_error_messages_helper(album)
      html = ""
      html << '<div class="errors">'
      album.errors.each_error do |attr, error|
        if !(attr =~ /\./)
          html << '<div class="error">'
          html << error.full_message
          html << '</div>'
        end
      end
     html << '</div>'
    end
    

    它将跳过名称包含“。”的属性的所有错误。 - 所以它应该打印与父对象关联的所有错误。唯一的问题是添加到基础的错误 - 因为它们 attr 的值是 base 并且我不确定如何将错误添加到子对象的基础中添加到父对象中的错误。可能他们attr 的值也是base 所以他们将被打印在这个助手中。不过不用add_to_base也没问题。

    【讨论】:

    • 未测试,但非常感谢您的全面回答!
    【解决方案2】:

    首先 f 和 f 混淆了范围。在您的 fields_for 部分使用“g”或其他内容,它将了解范围。

    那就试试吧:

    <% form_for @album do |f| %>
      <%= f.error_messages %>
    
      <%= f.label :title %><br />
      <%= f.text_field :title %>
    
      <% f.fields_for :tracks do |g, track| %>
        <%= g.error_messages -%>
        <%= render :partial => 'tracks/fields', :locals => {:f => g} %>
      <% end %>
    
      <%= f.submit 'Create' %>
    <% end %>
    

    在您的专辑模型上将此与 accepts_nested_attributes_for :tracks 一起使用。

    我自己对此进行了测试,并且可以正常工作。各个轨道的错误显示在轨道部分中。

    我正在使用最新版本的 Rails。

    【讨论】:

    • 它显示了整个表单(包括父表单)的错误消息,而不仅仅是那个特定的表单(这是我正在寻找的)。
    • 我已经用一个示例项目对此进行了测试,它工作得很好。与众不同的是“g”。
    • ...可能还有您正在使用的 Rails 版本 :)
    【解决方案3】:

    你试过了吗?

    <% f.fields_for :tracks do |f, track| %>
      <%= error_messages_for "track" %>
      <%= render :partial => 'tracks/fields', :locals => {:f => f} %>
    <% end %>
    

    【讨论】:

    • 是的!但是,没有出现表单错误。也没有 ruby​​ 错误...似乎有效,但似乎不起作用
    • 是的 - 问题是页面上没有名为 @track 甚至 @album.track 的变量(这是 error_messages_for 寻找的) - 相反你有 @album.tracks 和这简直令人困惑。
    • 我该如何解决?由于我正在循环播放曲目,因此有一个名为track 的实例,但不是@track(没有@ 符号)。我会开始赏金以鼓励更多的回应,如果你走在正确的轨道上,你可以得到它;)谢谢!
    • 这将起作用: track %>
    【解决方案4】:

    与上面类似,但是,使用下面的代码,我可以测试任何对象或对象数组是否有错误:

    <%= show_errors( [@company, @estabs], :header_ref => 'Company' ) %>
    

    def show_errors(objarray, opt)
    
        return '' if objarray.blank?
    
        err = [ ]
        html  = ""
    
        objarray.each { |obj|  
                if obj.is_a?(Array)
                    obj.each { |oo| html << show_errors([oo], opt) }
                elsif (obj.errors.count > 0 ) 
                    err << obj.errors.full_messages #didnt worked with gem i18n 0.5.0
                end
                  }
    
        return '' if (err.count==0) and (html.blank?)
    
        if err.count > 0
            err.flatten!            #remove sub-arrays
            err.each { |x| x.strip! }   #strip:remove espaces
            err.uniq!               #remove duplicated messages
            err.sort!
    
            header_ref = opt[:header_ref].nil?  ?  ""  :  opt[:header_ref]
    
            erro_str = (err.count == 1) ? 'erro' : 'erros'
            erro_verb = (err.count == 1) ? 'ocorreu' : 'ocorreram'
    
            html << '<div class="erros"  id="erros"> '
            html <<  "<b> #{header_ref}: #{err.count.to_s}  #{erro_str} #{erro_verb}: </b> "
            html << '<ul> ' 
    
            err.each  { |e| html << '<li> ' + e + ' </li>' }
    
            html << '</ul> </div>'
        end
        return html
    end
    

    【讨论】:

      猜你喜欢
      • 2017-06-20
      • 1970-01-01
      • 1970-01-01
      • 2022-01-21
      • 2016-10-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多