views:

580

answers:

3

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!

A: 

Did you try this?

<% f.fields_for :tracks do |f, track| %>
  <%= error_messages_for "track" %>
  <%= render :partial => 'tracks/fields', :locals => {:f => f} %>
<% end %>
Gordon Isnor
yeup! no form errors appears, though. No ruby errors either... seems to be valid but does not seem to be working
yuval
Ya - the problem with this is there's no variable called `@track` or even `@album.track` on the page (which is what `error_messages_for` looks for) - instead you've got `@album.tracks` and that's just plain confusing.
Taryn East
how would I solve it? Since I'm looping through the tracks, there's an instance called `track`, but not `@track` (without the `@` sign). I'll start a bounty to encourage some more responses and you can have it if you're on the right track ;) thanks!
yuval
This will work: <%= error_messages_for "track", :object => track %>
nanda
A: 

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.

Taryn East
it shows the error message for the whole form (including the parent form), not just that specific form (which is what I'm looking for).
yuval
I have tested this with an example project and it works just fine.It's the "g" that makes the difference.
Taryn East
...and possibly the version of rails that you are using :)
Taryn East
+2  A: 

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.

klew
Not tested, but thank you very much for the comprehensive answer!
yuval
I tested it on Rails 2.3.5 and it works as advertised ;)
klew