Я пытался создать сложную форму со многими вложенными моделями и сделать ее динамичной. Теперь я нашел, что создание вложенной модели не является трудным с accepts_nested_attributes_for, но создание его вложило, и динамичный было на вид невозможно, если было несколько вложенных моделей.
Я столкнулся с http://github.com/ryanb/complex-form-examples/blob/master/app/helpers/application_helper.rb, который делает это очень изящно. Кто-либо мог пролить некоторый свет на строки 13 и 16?
13 form_builder.object.class.reflect_on_association(method).klass.new
и
16 form_builder.fields_for(method, options[:object], :child_index => "new_#{method}") do |f|
От интуиции строка 13 инстанцирует нового объекта, но почему это должно сделать столько вызовов метода? Я не мог найти документацию для: опция child_index на строке 16. Когда форма создается, очень большое количество используется в качестве индекса для новых моделей, тогда как существующие модели индексируются их идентификатором. Как это работает?
options[:object] ||= form_builder.object.class.reflect_on_association(method).klass.new
Если объект не передан в качестве параметра, создается новый объект соответствующего класса.
form_builder.object
получает главный объект из формы (project
)
form_builder.object.class
получает его класс (Project
)
method
- имя ассоциации для Project (:tasks
)
reflect_on_association(method) дает объект AssociationReflection.
reflect_on_association(method). klass возвращает класс ассоциации. (Task
)
И, наконец, klass.new создает новый экземпляр (Task.new
=> new task)
Так сделано потому, что имя ассоциации не всегда является просто множественным именем класса, как в данном случае (Task - :tasks), а необходимо получить объект класса, чтобы создать новый экземпляр.
form_builder.fields_for(method, options[:object], :child_index => "new_#{method}") do |f|
:child_index
опция позволяет указать индекс конкретного объекта:
project[tasks_attributes][:child_index][field_name]
Он устанавливается в `"new_#{method}" Rails, но затем заменяется 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)
});
}
Индекс устанавливается в большое число, чтобы не конфликтовать с существующими объектами. Кстати, они не индексируются по id, просто от 0 до count-1