2010-02-27 3 views
2

J'ai deux modèles: Album et Track. L'album contient de nombreuses pistes et Track appartient à l'album. Je voudrais avoir la possibilité de créer autant de pistes que nécessaire tout en créant l'album, similaire à railscasts episode 197. Contrairement à l'épisode des rails, le formulaire Piste contient à la fois un titre et une description - les deux sont obligatoires.Comment utiliser error_messages_for dans un formulaire multi-modèle?

En ce moment, la forme ressemble à ceci:

Create New Album 

Name: [  ] 

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

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

(add track link) 

Si je décide de soumettre le formulaire vierge, je reçois des messages d'erreur suivants sur le dessus de la forme:

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

Ces erreurs les messages ne sont pas spécifiques au modèle, tous situés en haut de la page et apparaissent une seule fois pour chaque modèle (notez que j'ai laissé les champs à la fois vide et les messages d'erreur n'apparaissent qu'une fois - pas spécifique à quelle piste) .


Pour générer les champs de piste initiale, j'ai ajouté la ligne suivante dans la nouvelle action de mon album_controller: 2.times { @album.tracks.build }

L'essentiel de ce que ma forme ressemble est ceci:

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

J'ai essayé de remplacer le <%= f.error_messages %> supérieur avec <%= error_messages_for @album %> (pour afficher seulement les messages pour l'album), et en ajoutant un <%= error_messages_for track %> (pour afficher les messages d'erreur spécifiques à chaque piste) - mais cela ne fait pas l'affaire. Est-ce que quelqu'un sait comment aborder cela?

Merci!

Répondre

3

Si vous souhaitez séparer les messages d'erreur pour les objets parent et enfant, cela peut être un peu compliqué. Depuis que vous enregistrez l'objet parent, il valide également les objets enfants et contient des erreurs pour les enfants. Ainsi, vous pouvez faire quelque chose comme ceci:

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

Ou vous pouvez mettre cette ligne avec t.error_messages en 'tracks/fields' partielle (formulaire I retitré objet générateur de formulaire f-t parce qu'il était source de confusion). Il devrait afficher (au moins cela fonctionne pour moi) seulement des erreurs pour un objet enfant spécifique (ainsi vous pouvez voir quelle erreur de titre est pour quel objet).Gardez à l'esprit que Rails automaticaly ajouter la classe css fieldWithErrors aux champs qui contient des erreurs, sur exemple ajouter à css:

.fieldWithErrors { 
    border: 1px solid red; 
} 

Avec des erreurs pour objet parent, il est plus compliqué car @album.errors contient aussi les erreurs pour les objets enfants. Je ne trouve pas de façon agréable et simple à supprimer certaines erreurs ou pour afficher uniquement les erreurs qui sont associatted avec objet parent, donc mon idée était d'écrire aide personnalisée à manipuler:

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 

Il va ignorer les erreurs qui sont pour attribut qui nom conatins '. - Il devrait donc imprimer toutes les erreurs associées à l'objet parent. Le seul problème est avec les erreurs qui sont ajoutées à la base - puisqu'elles attr la valeur est base et je ne sais pas comment les erreurs ajoutées à la base de l'objet enfant ont été ajoutées aux erreurs dans l'objet parent. Probablement attr valeur est également base donc ils seront imprimés dans cette aide. Mais ce ne sera pas un problème si vous n'utilisez pas add_to_base.

+0

Non testé, mais merci beaucoup pour la réponse complète! –

+0

Je l'ai testé sur Rails 2.3.5 et ça marche comme annoncé;) – klew

0

Avez-vous essayé?

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

yeup! aucune erreur de forme n'apparaît, cependant. Pas d'erreur ruby ​​non plus ... semble être valide mais ne semble pas fonctionner –

+0

Ya - le problème avec ceci est qu'il n'y a pas de variable appelée '@ track' ou même' @ album.track' sur la page (qui est quoi ' error_messages_for' cherche) - à la place, vous avez '@ album.tracks' et c'est tout simplement confus. –

+0

comment pourrais-je le résoudre? Comme je parcours les pistes, il y a une instance appelée 'track', mais pas' @ track' (sans le signe '@'). Je vais commencer une prime pour encourager plus de réponses et vous pouvez l'avoir si vous êtes sur la bonne voie;) merci! –

1

Premièrement, f et f sont source de confusion pour la portée. utilisez "g" ou quelque chose pour votre section fields_for et il comprendra la portée.

Ensuite, essayez:

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

utiliser avec accepts_nested_attributes_for :tracks sur votre modèle d'album.

Je l'ai testé moi-même et cela fonctionne. Les erreurs pour les pistes individuelles apparaissent dans la section pour les pistes. J'utilise la dernière version de Rails.

+0

il montre le message d'erreur pour l'ensemble du formulaire (y compris le formulaire parent), pas seulement ce formulaire spécifique (qui est ce que je suis à la recherche de). –

+0

J'ai testé cela avec un exemple de projet et cela fonctionne très bien. C'est le "g" qui fait la différence. –

+0

... et éventuellement la version des rails que vous utilisez :) –

0

similaires ci-dessus, mais, avec le code ci-dessous, je peux tester un objet ou un tableau d'objets pour les erreurs:

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