Я бьюсь головой об этом в течение многих лет, и я только сегодня придумал то, чем я горжусь - элегантный, ненавязчивый, и вы можете сделать это только два или три (очень долго) строки кода.
ParentFoo has_many NestedBars, с соответствующими acceptpts_nested_parameters и всеми этими вкусностями. Вы отображаете набор полей для nested_bar в атрибут data-add-nested в ссылке, используя: child_index, чтобы установить строку, которую вы можете заменить позже. Вы передаете эту строку во второй параметр data-add-nested-replace
parent_foos / _form.html.haml
- semantic_form_for @parent_foo do |f|
-# render existing records
- f.semantic_fields_for :nested_bars do |i|
= render 'nested_bar_fields', :f => i
# magic!
- reuse_fields = f.semantic_fields_for :nested_bars, @parent_foo.nested_bars.build, :child_index => 'new_nested_bar_fields' do |n| render('nested_bar_fields', :f => n) end
= link_to 'Add nested bar', '#', 'data-add-nested' => reuse_fields, 'data-add-nested-replace' => 'new_nested_bar_fields'
частичные вложенные поля ничего особенного
parent_foos / _nested_bar_fields.html.haml
- f.inputs do
= f.input :name
= f.input :_delete, :as => :boolean
В вашем jQuery вы связываете щелчок элементов с полем для добавления данных (я использовал здесь live (), чтобы формы, загруженные ajax, работали), чтобы вставить поля в DOM, заменяя строку новый идентификатор. Здесь я делаю простую вещь, вставляя новые поля перед () ссылкой, но вы также можете указать в ссылке атрибут add-nested-replace-target, указывающий, где в DOM вы хотите, чтобы новые поля свернулись.
application.js
$('a[data-add-nested]').live('click', function(){
var regexp = new RegExp($(this).data('add-nested-replace'), "g")
$($(this).data('add-nested').replace(regexp, (new Date).getTime())).insertBefore($(this))
})
Вы могли бы поместить это в помощника, конечно; Я представляю это здесь непосредственно в представлении, так что принцип ясен без использования метапрограммирования. Если вас беспокоит конфликт имен, вы можете сгенерировать уникальный идентификатор в этом помощнике и передать его в виде nested-replace.
Придумывание поведения удаления будет оставлено читателю в качестве упражнения.
(показано для Rails 2, потому что это сайт, над которым я работаю сегодня - почти то же самое в Rails 3)