2010-05-21 4 views
21

J'ai essayé de créer une forme complexe avec de nombreux modèles imbriqués, et de la rendre dynamique. Maintenant, j'ai trouvé que faire un modèle imbriqué n'est pas difficile avec accept_nested_attributes_for, mais le rendre imbriqué et dynamique était apparemment impossible s'il y avait plusieurs modèles imbriqués. Je suis tombé sur http://github.com/ryanb/complex-form-examples/blob/master/app/helpers/application_helper.rb qui le fait très élégamment. Quelqu'un pourrait-il faire la lumière sur les lignes 13 et 16?Rails fields_for: explication de l'option child_index

13 form_builder.object.class.reflect_on_association(method).klass.new 

et

16 form_builder.fields_for(method, options[:object], :child_index => "new_#{method}") do |f| 

De l'intuition, la ligne 13 instancie un nouvel objet, mais pourquoi faut-il faire tant d'appels de méthode? Je n'ai trouvé aucune documentation pour l'option: child_index à la ligne 16. Lorsque le formulaire est créé, un très grand nombre est utilisé comme index pour les nouveaux modèles, alors que les modèles existants sont indexés par leur identifiant. Comment cela marche-t-il?

Répondre

35
options[:object] ||= form_builder.object.class.reflect_on_association(method).klass.new 

Si l'objet n'est pas transmis en tant que paramètre, il crée un nouvel objet de la classe correspondante.

form_builder.object obtient l'objet principal de la forme (project)

form_builder.object.class obtient sa classe (Project)

method est le nom de l'association pour le projet (:tasks)

reflect_on_association(method) donne objet AssociationReflection.

reflect_on_association (méthode). klass renvoie la classe de l'association. (Task)

Et, enfin, klass.new crée une nouvelle instance (Task.new => nouvelle tâche)

Il est fait de cette façon parce que le nom de l'association est pas toujours juste un nom de classe Pluralisé comme dans ce cas (Tâche -: tâches), mais il est nécessaire d'obtenir l'objet de classe pour créer une nouvelle instance de celui-ci.

form_builder.fields_for(method, options[:object], :child_index => "new_#{method}") do |f| 

l'option :child_index vous permet de spécifier l'index de l'objet particulier:

project[tasks_attributes][:child_index][field_name] 

Il est réglé sur ` "nouveau _ # {méthode}" par Rails, mais est ensuite remplacé par javascript (application. js):

function insert_fields(link, method, content) { 
    var new_id = new Date().getTime(); 
    var regexp = new RegExp("new_" + method, "g") 
    $(link).up().insert({ 
    before: content.replace(regexp, new_id) 
    }); 
} 

L'index est défini sur un grand nombre pour ne pas entrer en conflit avec des éléments existants. Ils ne sont pas indexés par id de la même manière, juste de 0 à count-1

+0

Comment Rails sait-il quoi effacer dans la base de données s'il est indexé de 0 à count - 1? Il me manque quelque chose. – Timothy

+4

Il insère un champ caché pour chaque enregistrement, contenant son id: ''. C'est une bonne idée de voir la source HTML pour comprendre, ce que Rails génère. – Voyta

+0

excellente explication. Merci. – user938363