2010-04-20 11 views
24

Wszyscy widzieliśmy genialny complex forms railscast, w którym Ryan Bates wyjaśnia, jak dynamicznie dodawać lub usuwać zagnieżdżone obiekty w formularzu obiektu macierzystego za pomocą Javascript.Jak dynamicznie dodawać i usuwać zagnieżdżone pola modelu za pomocą Haml i Formtastic

Czy ktoś ma jakieś pomysły na temat tego, jak te metody muszą zostać zmodyfikowane, aby pracować z Haml Formtastic?

Aby dodać niektóre kontekst tutaj jest uproszczoną wersją problemu Jestem obecnie stoi:

formularza # Nauczyciel (który ma zagnieżdżone form Temat) [z mojej aplikacji]

- semantic_form_for(@teacher) do |form| 
    - form.inputs do 
    = form.input :first_name 
    = form.input :surname 
    = form.input :city 
    = render 'subject_fields', :form => form 
    = link_to_add_fields "Add Subject", form, :subjects 

# osobnika tworzyć częściową [mojego stosować]

- form.fields_for :subjects do |ff| 
    #subject_field 
    = ff.input :name 
    = ff.input :exam 
    = ff.input :level 
    = ff.hidden_field :_destroy 
    = link_to_remove_fields "Remove Subject", ff 

# aplikacji pomocniczej (prosty z Railscasts)

def link_to_remove_fields(name, f) 
    f.hidden_field(:_destroy) + link_to_function(name, "remove_fields(this)") 
    end 

    def link_to_add_fields(name, f, association) 
    new_object = f.object.class.reflect_on_association(association).klass.new 
    fields = f.fields_for(association, new_object, :child_index => "new_#{association}") do |builder| 
     render(association.to_s.singularize + "_fields", :f => builder) 
    end 
    link_to_function(name, h("add_fields(this, \"#{association}\", \"#{escape_javascript(fields)} \")")) 
    end 

# Application.js (prosto z Railscasts)

function remove_fields(link) { 
    $(link).previous("input[type=hidden]").value = "1"; 
    $(link).up(".fields").hide(); 
    } 

function add_fields(link, association, content) { 
    var new_id = new Date().getTime(); 
    var regexp = new RegExp("new_" + association, "g") 
    $(link).up().insert({ 
    before: content.replace(regexp, new_id) 
    }); 
    } 

Problem z realizacji wydaje się być z metodami JavaScript - drzewo DOM z Formtastic postaci znacznie różni się od zwykłej postaci szyn.

Widziałem to pytanie kilka razy w Internecie, ale jeszcze nie znalazłem odpowiedzi - teraz wiesz, że pomoc docenią nie tylko mnie!

Jack

+0

można zakładać wyjście HTML swojego 'fields_for: subjects'? – nfm

Odpowiedz

25

Jesteś na właściwej drodze:

... drzewa DOM z Formtastic formie znacznie różni się od zwykłego szyn formie.

Aby dostosować przykładem Ryana dla formtastic, jest to pomocne do przypomnienia, że ​​semantic_fields_for pomocnik jest podobna do semantic_form_for pomocnika, który wyprowadza wejść w formie listy.

Aby zachować rzeczy tak blisko jak to możliwe kodu Railscast, musisz:

  • ująć kolekcję pól zagnieżdżonych w opakowaniu
  • (używam div z identyfikatorem subjects CSS).
  • załączyć zagnieżdżonych pól w opakowaniu ul/ol (I zastosowano klasę nested-fields CSS.)

Oto co pliki powinny się podoba.

forma Nauczyciel (z zagnieżdżonych pól tematu):

- semantic_form_for(@teacher) do |form| 
    - form.inputs do 
    = form.input :first_name 
    = form.input :surname 
    = form.input :city 

    %h2 Subjects 
    #subjects 
     - form.semantic_fields_for :subjects do |builder| 
     = render :partial => "subject_fields", :locals => { :f => builder } 
     .links 
     = link_to_add_fields "Add Subject", form, :subjects 

pola Temat częściowy (dla zagnieżdżonych tematu):

%ul.nested-fields 
    = f.input :name 
    = f.input :exam 
    = f.input :level 
    = link_to_remove_fields "Remove Subject", f 

ApplicationHelper:

def link_to_remove_fields(name, f) 
    f.hidden_field(:_destroy) + link_to_function(name, "remove_fields(this)") 
end 

def link_to_add_fields(name, f, association) 
    new_object = f.object.class.reflect_on_association(association).klass.new 
    fields = f.fields_for(association, new_object, :child_index => "new_#{association}") do |builder| 
    render(association.to_s.singularize + "_fields", :f => builder) 
    end 
    link_to_function(name, h("add_fields(this, \"#{association}\", \"#{escape_javascript(fields)}\")")) 
end 

Application.js:

function remove_fields(link) { 
    $(link).previous("input[type=hidden]").value = "1"; 
    $(link).up(".nested-fields").hide(); 
} 

function add_fields(link, association, content) { 
    var new_id = new Date().getTime(); 
    var regexp = new RegExp("new_" + association, "g") 
    $(link).up().insert({ 
    before: content.replace(regexp, new_id) 
    }); 
} 

użyciu następujących perełki:

  • formtastic (0.9.10)
  • haml (3.0.2)
  • korniszona (1.0.26)
  • szyny (2.3.5)
+0

Dzięki za rozwiązanie - więc pola tematów są wyświetlane, podobnie jak przyciski, ale przewijam je na górę ekranu bez żadnej akcji, gdy kliknę link (mam js domyślnie włączony w przypadku, gdy się zastanawiasz). Wypróbowałem rozwiązanie Steve'a i działa link usuwający, ale niestety nie przycisk dodawania. Może się zdarzyć, że mój znacznik będzie trochę inny - używając Rails 3 i najnowszej Formtastic. Jakieś pomysły? –

+0

Próbowałem tylko tej konfiguracji z klejnotami, które wymieniłem powyżej. – Bennett

+0

Ponadto, nie brzmi to tak, jakbyś dokładnie przestrzegał mojego przykładu, aby Twoja struktura była inaczej skonstruowana. Zalecam, aby najpierw uzyskać pomocnika link_to_add_fields i add_fields, zanim przejdziesz do usuwania pól. – Bennett

3

application.js dla jQuery:

function remove_fields(link) { 
    $(link).prev("input[type=hidden]").val("1"); 
    $(link).parent(".nested-fields").hide(); 
} 

function add_fields(link, association, content) { 
    var new_id = new Date().getTime(); 
    var regexp = new RegExp("new_" + association, "g") 
    $(link).parent().before(content.replace(regexp, new_id)); 
} 
+0

Dzięki - przycisk usuwania działał dobrze, ale nie przycisk Dodaj .... za pomocą Rails3 i najnowszej Formtastic. –

1

W Railsach 3 nie ma potrzeby używania funkcji h() w helperie. Po prostu pomiń i powinno działać.

1

Od wielu lat biję się przeciwko temu, a ja właśnie wymyśliłem coś, z czego jestem dumny - elegancki, dyskretny, i możesz to zrobić tylko dwa lub trzy (naprawdę długo) linie kodu.

ParentFoo has_many NestedBars, z odpowiednimi parametrami accepts_nested_parameters i wszystkimi tymi smakołykami. Wyrenderujesz zestaw pól dla zagnieżdżonego_baru w atrybucie zagnieżdżania danych na łączu, używając: child_index do ustawienia ciągu, który możesz później wymienić. Przekazać ten ciąg w drugim parametrze danych dodatek nested-zastąpić

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' 

zagnieżdżonych obszarach częściowych jest nic nadzwyczajnego

parent_foos/_nested_bar_fields.html .haml

- f.inputs do 
    = f.input :name 
    = f.input :_delete, :as => :boolean 

W swojej jQuery powiązać kliknięcie elementów z-add-zagnieżdżone pola danych (Użyłem żywo() tutaj więc formy AJAX załadowana będzie działać), aby wstawić pola i nto DOM, zastępując ciąg znaków nowym ID. Tutaj robię prostą rzecz wstawiania nowych pól przed() odsyłaczem, ale możesz również podać atrybut add-nested-replace-target w linku informujący, gdzie w DOM chcesz, aby nowe pola się skończyły.

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)) 
}) 

Można umieścić to w pomocnika, oczywiście; Przedstawiam to tutaj bezpośrednio w widoku, więc zasada jest jasna, bez zgrzytania w metaprogramowaniu. Jeśli obawiasz się kolizji nazw, możesz wygenerować unikalny identyfikator w tym pomocniku i przekazać go w zastępowaniu zagnieżdżonym.

wymyślanie zachowania związanego z usunięciem jest pozostawione jako ćwiczenie dla czytelnika.

(pokazane na szynach 2, ponieważ jest to miejsce pracuję na dziś - prawie taka sama w Rails 3)

+0

uwaga: w Railsach 3 może nie być tak łatwo, dzięki ucieczce html :( –

+0

używanie escape_once wydaje się działać, jak w tym półpowiązanym przykładzie z wstawieniem pojedynczego pola: - new_field = escape_once f.input (: related_thing) = link_to 'Dodaj kolejny', '#', 'data-insert' => new_field –

+1

Opracowałem tę technikę nieco dalej, do wykorzystania bez modelu - Miałem przypadek, w którym klient chciał określić zestawy pól do wyświetlenia w raporcie Ta wersja używa bloku zamiast oddzielnego szablonu dla pól i używa generycznego modelu do generowania wymiennego parametru. Oto treść: https://gist.github.com/1554795 –

Powiązane problemy