PicoJournal – Ajax in Rails ( + Paperclip)

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.

pfeildings

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 😉

Animation
Styles sind noch unwichtig :p

pfeildings

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!

Animation2
Post taucht auf! Bild geht noch nicht.

pfeildings

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!

pfeildings

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.

Animation4
Datumsauswahl ist natürlich noch Quatsch

 

pfeildings

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*