如何在多模型形式中使用 error_messages_for ?

发布于 2024-08-23 07:33:40 字数 1456 浏览 5 评论 0原文

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

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

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


为了生成初始轨道字段,我在 album_controller 的新操作中添加了以下行: 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 %> (以显示特定于每个轨道的错误消息) - 但这并不能解决问题。 有人知道如何解决这个问题吗?

谢谢!

I have two models: Album and Track. Album has many tracks, and Track belongs to album.

I'd like to have the ability to create as many tracks as needed while creating the album, similailiy to railscasts episode 197. Unlike the railscasts episode, though, the Track form contains both a title and a description - both are required.

Right now, the form looks like this:

Create New Album

Name: [      ]

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

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

(add track link)

If I decide to submit the form blank, I get the following error messages on top of the form:

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

These error messages are not specific to the model, all located at the top of the page, and appear only once for each model (note that I left the fields for both blank and the error messages appear only once - not specific to which track).


To generate the initial track fields, I added the following line in the new action of my album_controller: 2.times { @album.tracks.build }

The gist of what my form looks like is this:

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

I tried replacing the top <%= f.error_messages %> with <%= error_messages_for @album %> (to only display the messages for the album), and adding a <%= error_messages_for track %> (to display the error messages specific to each track) - but this does not do the trick.
Does anybody know how to approach this?

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

撑一把青伞 2024-08-30 07:33:41

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

<% 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< /code> 还包含子对象的错误。我没有找到任何好的简单方法来删除一些错误或仅显示与父对象关联的错误,所以我的想法是编写自定义帮助程序来处理它:

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,这不会有问题。

If you want to separate error messages for parent and child object it can be a little complicated. Since when you save parent object it also validates child objects and it contains errors for children. So you can do something like this:

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

Or you can put this line with t.error_messages in 'tracks/fields' partial (I renamed form builder object form f to t because it was confusing). It should display (at least it works for me) only errors for specific child object (so you can see what title error is for which object). Also keep in mind, that Rails will automaticaly add css class fieldWithErrors to fields that contains errors, on example add to css:

.fieldWithErrors {
  border: 1px solid red;
}

With errors for parent object it is more complicated since @album.errors contains also errors for child objects. I didn't find any nice and simple way to remove some errors or to display only errors that are associatted with parent object, so my idea was to write custom helper to handle it:

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

It will skip all errors that are for attribute which name conatins '.' - so it should print all errors that are associated with the parent object. The only problem is with errors that are added to base - since they attr value is base and I'm not sure how are errors added to base to child object added to errors in parent object. Probably they attr value is also base so they will be printed in this helper. But it won't be problem if you don't use add_to_base.

人│生佛魔见 2024-08-30 07:33:41

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

Firstly f and f are confusing for scope. use "g" or something for your fields_for section and it will understand the scope.

Then try:

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

Use this with accepts_nested_attributes_for :tracks on your album model.

I have tested this myself and it works. The errors for the individual tracks appear within the section for the tracks.

I am using the latest version of Rails.

千年*琉璃梦 2024-08-30 07:33:41

你尝试过这个吗?

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

Did you try this?

<% f.fields_for :tracks do |f, track| %>
  <%= error_messages_for "track" %>
  <%= render :partial => 'tracks/fields', :locals => {:f => f} %>
<% end %>
等待圉鍢 2024-08-30 07:33:41

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

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

Similar to above, but, with the code below, I can test any object or array of objects for errors:

<%= 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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文