Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails fields_for :child_index option explanation

I have been trying to create a complex form with many nested models, and make it dynamic. Now I found that making a nested model isn't difficult with accepts_nested_attributes_for, but making it nested and dynamic was seemingly impossible if there were multiple nested models.

I came across http://github.com/ryanb/complex-form-examples/blob/master/app/helpers/application_helper.rb which does it very elegantly. Could anyone shed some light on lines 13 and 16?

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

and

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

From intuition, line 13 instantiates a new object, but why must it do so many method calls? I couldn't find any documentation for the :child_index option on line 16. When the form is created, a very large number is used as an index for new models, whereas existing models are indexed by their id. How does this work?

like image 539
Timothy Avatar asked May 21 '10 02:05

Timothy


1 Answers

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

If the object is not passed as a parameter, it creates a new object of the corresponding class.

form_builder.object gets the main object from the form (project)

form_builder.object.class gets its class (Project)

method is the association name for the Project (:tasks)

reflect_on_association(method) gives AssociationReflection object.

reflect_on_association(method).klass returns the class of the association. (Task)

And, finally, klass.new creates a new instance (Task.new => new task)

It is done this way because the association name is not always just a pluralized class name like in this case (Task - :tasks), but it is necessary to get the class object to create new instance of it.

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

:child_index option allows you to specify the index of particular object:

project[tasks_attributes][:child_index][field_name] 

It is set to `"new_#{method}" by Rails, but is then replaced by 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)   }); } 

Index is set to a large number just not to conflict with existing items. They are not indexed by id by the way, just from 0 to count-1

like image 196
Voyta Avatar answered Sep 27 '22 18:09

Voyta