2

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

我希望能够在创建专辑时根据需要创建尽可能多的曲目,类似于railscasts 第 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

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


为了生成初始曲目字段,我在专辑控制器的新操作中添加了以下行: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 %>

我尝试将顶部替换为<%= f.error_messages %><%= error_messages_for @album %>仅显示专辑的消息),并添加一个<%= error_messages_for track %>(显示特定于每个曲目的错误消息)——但这并不能解决问题。有人知道如何解决这个问题吗?

谢谢!

4

4 回答 4

3

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

<% 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'部分中(我将表单构建器对象表单重命名为ft因为它令人困惑)。它应该只显示特定子对象的错误(至少它对我有用)(这样你就可以看到哪个对象的标题错误)。还要记住,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

于 2010-03-03T14:16:53.670 回答
1

首先 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。

于 2010-02-27T22:32:15.760 回答
0

你试过这个吗?

<% f.fields_for :tracks do |f, track| %>
  <%= error_messages_for "track" %>
  <%= render :partial => 'tracks/fields', :locals => {:f => f} %>
<% end %>
于 2010-02-27T04:44:47.270 回答
0

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

<%= 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
于 2011-01-18T18:41:11.063 回答