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?
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With