Die Idee ist simpel. Neue Posts sollen ohne erneutes Laden der Seite erstellbar und editierbar sein. Wie durch Magie taucht ein neu erstellter Post direkt an der Stelle auf, an der eben noch das Formular dafür war. Sehr viele Stunden später habe ich es geschafft dies zumindest für das Erstellen von neuen Einträgen umzusetzen. Ich zeige jetzt kurz wie das im einfachsten Fall geht und was man machen kann, wenn man Bilder hinzufügen will oder mehrere Formulare auf einer Seite hat.
Der einfachste Fall – Ein Formular erscheinen lassen
Zu Beginn steht ein falsches Versprechen: Ajax funktioniert in Rails supereinfach. Es muss nur remote: true zum Formular hinzugefügt werden! Stimmt so leider nicht, doch der einfachste Fall bleibt trotzdem noch überschaubar. Hier alle Schritte um einen Ajax Call zu machen und dynamisch ein Formular zum Erstellen eines neuen Posts anzuzeigen (bei mir heißen die Posts ‚Days‘).
1. Link remote:true oder data-remote=“true“ hinzufügen
Je nachdem welche Link-Art du nutzt. Eine id zum Ansprechen wird im nächsten Schritt wichtig.
<a href="/days/new" data-remote="true" id="new_day">Add This day</a>
oder
<%= link_to 'New', new_day_path, id: "new_day", remote:true %>
2. Eine new.js.erb Datei erstellen
Hier steht der Code, der beim Klicken des Links ausgeführt wird. Ich möchte den Link verbergen und an der Stelle das Formular anzeigen, Das ‚j‘ ist zum escapen.
$('#new_day').hide().after('<%= j render("form")%>');
3. Fertig
So lässt sich ein Formular dynamisch anzeigen! Ab hier wird es nur noch schlimmer 😉
Der einfachste Fall 2 – Einen neuen Post erscheinen lassen
Für den ‚Create‘ Fall wird es nur ein bisschen schwerer, denn es wird eine Anpassung im Controller nötog. Halb so wild, wenn man weiß was zu tun ist.
1. Formularheader remote: true hinzufügen
Damit wird Ajax für den submit des Formulars aktiviert.
<%= form_for @day, html: {multipart: true}, remote: true do |f| %>
2. Controller anpassen
In der create Methode findet ihr wahrscheinlich ein format.html{} wieder. Dieses muss erweitert werden, damit der Controller auch auf Javascript hört. Dafür der respond_to Block.
def create @day = current_user.days.build(day_params) respond_to do |format| format.html { redirect_to @day, notice: 'Day was successfully created.' } format.js end end
3. create.js.erb anlegen
Hier entferne ich das Formular, zeige den Link zum Erscheinen des Formulars wieder an und füge den neuen Post in eine Liste namens all_days ein.
$('#new_day').remove(); $('#new_link').show(); $('#all_days').prepend('<%= j render(@day) %>');
4. Fertig
Nun lässt sich ein neuer Post komplett dynamisch erstellen!
Die erste Erweiterung – Mehrere dynamische IDs
Ich erzeuge auf einer einzelnen Seite mehrere Formularlinks für jeden nicht schon ausgefüllten Tag. Wie bekomme ich jetzt das Formular nur an die gewünschte Stelle?
1. Dynamische ID
Zuerst bekommen die Links eine dynamische ID von mir, damit ich immer die richtige Stelle im Ajax Code finde.
# Meine Post Loop start_date = current_user.created_at.to_date end_date = Date.today end_date.downto(start_date).each do |date| # [...] #Fügt das Datum an die ID an. Bsp: new_day_16-06-28 <a href="/days/new" data-remote="true" id="new_day_<%= date.strftime('%Y-%m-%d') %>">Add This day</a>
2. Daten per Parameter weitergeben
Damit ich im Javascript weiß, welche ID für den Aufruf verantwortlich ist, muss man etwas tricksen. Wir übergeben einen einzigartigen Parameter über die URL an. Das kann bei mir das Datum sein oder auch die Post ID. Der Link im vorigen Schritt wird also folgendermaßen erweitert.
<a href="/days/new?date=<%= date.strftime('%Y-%m-%d') %>" data-remote="true" id="new_day_<%= date.strftime('%Y-%m-%d') %>">Add This day</a>
3. new.js.erb mit Parameter erweitern
Aus der Erweiterung oben lässt sich das Datum nun folgendermaßen in Javascript auslesen. Dieses nutze ich um die richtige ID zusammenzupuzzlen.
$('#new_day_<%= params[:date] %>').hide().after('<%= j render("form")%>');
4. create.js.erb ändern
Hier musst du bestimmt selbst nochmal genau gucken wo welche Dinge auftauchen sollen. Bei meinem Beispiel orientiere ich mich vom Formular aus zum nächstgelegenen li-tag.
var temp = $('#new_day').closest( 'li' ); $('#new_day').remove(); $(temp).html('<%= j render(@day) %>').hide().fadeIn(); //ginge natürlich auch in einer Zeile
5. Fertig
Das Formular tauch nur an gewünschter Stelle auf und fügt hier einen Post ein!
Die zweite Erweiterung – Dateien mit Ajax
Alle vorigen Beispiele funktionieren nur mit Text, Dateien lassen sich nicht dynamisch hochladen und anzeigen. Hierfür gibt es eine Reihe Lösungen, die wirklich mal erstaunlich einfach ist das Gem remotipart.
Für meinen Fall genügte der folgende Schritt.
1. Remotipart im Gemfile hinzufügen
gem 'remotipart', '~> 1.2' #Lieber nochmal die Version nachgucken
und bundle install ausführen.
2. Fertig
Ich bin beim gewünschten Ergebnis angekommen! Zumindest was das Erstellen angeht.
Abschluss
Der hier gezeigte Weg ist bestimmt nicht der Beste, denn er ist das Ergebnis von stundenlangem Haareraufen und Ausprobieren. Auch habe ich es nicht mehr geschafft das Edit Formular dynamisch umzusetzen, das ich hierfür partials mit anhängenden lokalen Variablen rendern wollte. Das klappte nicht.
Auch nicht zu vergessen sind hier sehr viele nicht abgedeckte Corner Cases und die Fälle wenn Ajax nicht erfolgreich ist. Diese müsst eman sich nochmal näher ansehen.
Live kannst du den Code auch inspizieren (F12 im Browser) und zwar auf www.picojournal.com (Testserver, kann seeehr lahm sein).
Ich fühle mich total ausgelaugt, dieser Fortschritt war wirklich schwer. Ich hoffe ich kann das Ajax Thema noch mit weniger Aufwand abrunden und mich was leichterem widmen :p Aber jetzt erst mal entspannen. Bis bald!
*genießt seine 4 Tage Semesterferien*