Ponieważ nikt nie zaoferował odpowiedzi na to pytanie, nawet po bounty, w końcu udało mi się to zrobić samemu. To nie powinno być oszałamiające! Mam nadzieję, że łatwiej będzie to zrobić w Rails 3.0.
Przykład Andy'ego jest dobrym sposobem na usunięcie rekordów bezpośrednio, bez przesyłania formularza do serwera. W tym konkretnym przypadku, czego tak naprawdę szukam, jest sposób dynamicznego dodawania/usuwania pól przed wykonaniem aktualizacji do zagnieżdżonego formularza. Jest to nieco inny przypadek, ponieważ po usunięciu pól nie są one faktycznie usuwane, dopóki formularz nie zostanie przesłany. Prawdopodobnie wykorzystam oba w zależności od sytuacji.
Oparłem moją implementację na widelcu Tim Riley's complex-forms-examples na github.
najpierw skonfigurować modele, i upewnić się, że wspiera zagnieżdżone atrybuty:
class Person < ActiveRecord::Base
has_many :phone_numbers, :dependent => :destroy
accepts_nested_attributes_for :phone_numbers, :reject_if => lambda { |p| p.values.all?(&:blank?) }, :allow_destroy => true
end
class PhoneNumber < ActiveRecord::Base
belongs_to :person
end
stworzyć częściowy widok na pola formularza PhoneNumber za:
<div class="fields">
<%= f.text_field :description %>
<%= f.text_field :number %>
</div>
Następny podstawowego widoku edycji za Osoba model:
<% form_for @person, :builder => LabeledFormBuilder do |f| -%>
<%= f.text_field :name %>
<%= f.text_field :email %>
<% f.fields_for :phone_numbers do |ph| -%>
<%= render :partial => 'phone_number', :locals => { :f => ph } %>
<% end -%>
<%= f.submit "Save" %>
<% end -%>
To będzie działać, tworząc zestaw pól szablonu dla modelu PhoneNumber, który możemy powielić z javascript. Będziemy tworzyć metody pomocnika w app/helpers/application_helper.rb
na to:
def new_child_fields_template(form_builder, association, options = {})
options[:object] ||= form_builder.object.class.reflect_on_association(association).klass.new
options[:partial] ||= association.to_s.singularize
options[:form_builder_local] ||= :f
content_tag(:div, :id => "#{association}_fields_template", :style => "display: none") do
form_builder.fields_for(association, options[:object], :child_index => "new_#{association}") do |f|
render(:partial => options[:partial], :locals => { options[:form_builder_local] => f })
end
end
end
def add_child_link(name, association)
link_to(name, "javascript:void(0)", :class => "add_child", :"data-association" => association)
end
def remove_child_link(name, f)
f.hidden_field(:_destroy) + link_to(name, "javascript:void(0)", :class => "remove_child")
end
teraz dodać te metody pomocnika do edycji częściowej:
<% form_for @person, :builder => LabeledFormBuilder do |f| -%>
<%= f.text_field :name %>
<%= f.text_field :email %>
<% f.fields_for :phone_numbers do |ph| -%>
<%= render :partial => 'phone_number', :locals => { :f => ph } %>
<% end -%>
<p><%= add_child_link "New Phone Number", :phone_numbers %></p>
<%= new_child_fields_template f, :phone_numbers %>
<%= f.submit "Save" %>
<% end -%>
masz teraz szablonów js zrobić. Przekaże pusty szablon dla każdego powiązania, ale klauzula :reject_if
w modelu odrzuci je, pozostawiając tylko pola utworzone przez użytkownika. Aktualizacja:Ponownie przemyślałem ten projekt, patrz poniżej.
To naprawdę nie jest AJAX, ponieważ nie ma żadnej komunikacji na serwerze poza ładowaniem strony i wysyłania formularza, ale szczerze nie mogłem znaleźć sposobu, aby to zrobić po fakcie.
W rzeczywistości może to zapewnić lepsze wrażenia użytkownika niż AJAX, ponieważ nie musisz czekać na odpowiedź serwera dla każdego dodatkowego pola, dopóki nie skończysz.
Wreszcie musimy połączyć to z javascript. Dodaj poniższe linie do pliku `/ public/JavaScripts application.js':
$(function() {
$('form a.add_child').click(function() {
var association = $(this).attr('data-association');
var template = $('#' + association + '_fields_template').html();
var regexp = new RegExp('new_' + association, 'g');
var new_id = new Date().getTime();
$(this).parent().before(template.replace(regexp, new_id));
return false;
});
$('form a.remove_child').live('click', function() {
var hidden_field = $(this).prev('input[type=hidden]')[0];
if(hidden_field) {
hidden_field.value = '1';
}
$(this).parents('.fields').hide();
return false;
});
});
Do tego czasu trzeba mieć kadłubków formę dynamiczną! Tutaj javascript jest naprawdę prosty i można go łatwo wykonać za pomocą innych frameworków. Możesz łatwo zastąpić mój kod application.js
prototypem + lowpro na przykład. Podstawową ideą jest to, że nie wbudowujesz gigantycznych funkcji javascript w swój znacznik i nie musisz pisać żmudnych phone_numbers=()
funkcji w swoich modelach. Wszystko po prostu działa. Brawo!
Po jakimś dalszym badaniom, mam stwierdziła, że szablony muszą być przeniesione z pola <form>
. Utrzymanie ich w tym miejscu oznacza, że zostaną odesłani do serwera wraz z resztą formularza, co może później wywołać bóle głowy.
Dodałem to do głębi układ:
<div id="jstemplates">
<%= yield :jstemplates %>
</div
i zmodyfikowała new_child_fields_template
pomocnika:
def new_child_fields_template(form_builder, association, options = {})
options[:object] ||= form_builder.object.class.reflect_on_association(association).klass.new
options[:partial] ||= association.to_s.singularize
options[:form_builder_local] ||= :f
content_for :jstemplates do
content_tag(:div, :id => "#{association}_fields_template", :style => "display: none") do
form_builder.fields_for(association, options[:object], :child_index => "new_#{association}") do |f|
render(:partial => options[:partial], :locals => { options[:form_builder_local] => f })
end
end
end
end
Teraz można usunąć klauzule :reject_if
z modeli i przestać się martwić szablony są odesłane.
Czy masz problemy z pisaniem dyskretnie sferifikowanej jQuery ogólnie lub czy jest ona specyficzna dla zagnieżdżonych formularzy? Jeśli chcesz dyskretnie pozbyć się jRails, to dobry początek. –
Potrzebuję obsługi form zagnieżdżonych, ale staram się teraz uchwycić podstawy. Mam zainstalowane jRails, ale go nie używam; tak jak powiedziałem, chcę to robić dyskretnie i wolałabym nie musieć się uczyć kolejnego DSL, gdy jestem doskonale dobry w javascript. –