Achtung Technik: Unsere Methode für Live-Updates

Veröffentlicht am von Nicole Nicole auf Google+ in der Kategorie abiapp-Features, Hinter den Kulissen

Achtung, dies ist ein sehr technischer Beitrag, der nicht darauf ausgerichtet ist, für Laien verständlich zu sein. Wenn du dich nicht auskennst, kannst Du ihn natürlich trotzdem lesen – wir übernehmen aber keine Verantwortung :-) Bei technischen Fragen kannst du uns natürlich gern unter hallo@abiapp.net kontaktieren.

Kürzlich im abiapp.net-Changelog:

  • Fundamentale Überarbeitungen an unserer Technik, die dafür sorgen, dass noch mehr Dinge auf abiapp.net sich jetzt live ändern, ohne dass ihr die Seite neu laden müsst

Dahinter versteckt sich nicht weniger als die komplette Umstrukturierung und Neuimplementierung des beinahe kompletten JavaScript-Quelltextes von abiapp.net – ein Vorgang, der sich über einige Monate gestreckt hat. In diesem Beitrag wollen wir etwas mehr dazu erzählen und auch für die Technikinteressierten unter euch zeigen, wie man mit den neuen Techniken herumspielen kann. :)

Neben einigen typischen abiapp.net-UI-Werkzeugen wie den „fliegenden Namen“ im Adminbereich, editierbaren Feldern (die gelb hinterlegten) und dem automatischen Umleiten von Formularsendungen als AJAX-Request besteht der JavaScript-Code von abiapp.net hauptsächlich aus Zeilen, die unser Haupt-UI-Feature verantworten: Die Live-Änderungen. Jede veränderte Eigenschaft, jeder neue Kommentar, fast alles was bei abiapp.net passieren kann wird live bei allen Usern die online sind, angezeigt, ohne dass die Seite neu geladen werden muss.

Aktuell läuft diese Technik über sekündliche AJAX-Requests (auf mobilen Clients seltener; in Zukunft ist eine Implementierung per Websockets geplant). Bisher war das so implementiert, dass es für jede Seite, auf der sich etwas ändern konnte, eigenen Code gab. Die AJAX-Requests enthielten eine Beschreibung der Seite und der Server antwortete mit Änderungen, die seit der letzten Anfrage geschehen sind. Was zunächst am einfachsten war, entwickelte sich schnell zu einer gigantischen Menge an Fallunterscheidungen. Schnell war klar, dass dieses System eines der ersten war, dass einer gründlichen Überarbeitung unterzogen werden sollte. „js-models“ hieß unser git-Branch – und der Name ist Programm: Auf Serverseite setzen wir Django ein, das wie die meisten Datenbankabstraktionen mit sogenannten Models (abstrakten Beschreibungen der Datenstrukturen) gearbeitet wird – so gibt es bei uns beispielsweise Models für Personen, Kommentare, Umfragen, Umfrageoptionen und vieles mehr. Äquivalente zu diesen Models sind nun auch JavaScript-seitig im Einsatz.

Was heißt das? Der Einfachste Weg für dich, hier unter die Haube zu gucken, ist, in der Javascript-Konsole einfach mal objects einzugeben. Schon siehst du alle Objekte, die aktuell auf dieser Seite existieren – geordnet nach Model. Clientseitig sind hier alle bekannten Eigenschaften über auf der Seite vorhandene Objekte gespeichert. Diese Daten werden automatisch aus dem DOM der Seite beim Seitenaufbau generiert. Alle Objekte enthalten in einem data-object-HTML-Attribut ihr Model und in einem data-id-Attribut ihre ID. Die Javascript-Models sind in der Lage, aus den gerenderten Objekten ihre Eigenschaften aufzulesen und zu speichern. Das tolle: Das geht auch umgekehrt – jede Änderung an einer Eigenschaft wird sofort überall im DOM ebenfalls geändert. Mehr dazu gleich.

Zusätzlich zu Javascript-Models existieren nun auch Listen. Diese sind via lists (bzw lists_by_type) einsehbar. Auch sie werden anhand von Attributen wie data-list und data-filter beim Seitenaufbau initialisiert. Listen haben die Aufgabe, verschiedene Objekte eines Models aufzulisten. Dabei kann es Kriterien geben, nach denen die Objekte, die in dieser Liste aufgeführt werden, ausgewählt werden (bei Kommentaren auf einer Personenseite sollen zum Beispiel nur Kommentare zu dieser Person geführt werden). Auch die Anzeige der Elemente in einer bestimmten Reihenfolge ist möglich. Listen halten sich automatisch aktuell, neue oder geänderte Objekte, die in die Liste passen, werde der Liste hinzugefügt (wir nutzen dafür EJS als Implementierung für Javascript-Templates – die Javascript-Templates findest Du in <script type="text/template">-Tags im HTML-Quelltext). Gelöschte Objekte oder Objekte, die durch eine Änderung nicht mehr in die Liste gehören, werden aus der Liste entfernt. Auch die Sortierung wird immer aktuell gehalten. Selbstverständlich werden alle diese Änderungen automatisch einheitlich animiert.

Was bringen uns die ganzen Models nun? Zum ersten eine Vereinheitlichung und eine deutliche verbesserte Übersichtlichkeit der Codes. Aber vor allem bedeutet es für uns, dass wir jetzt viel einfacher und an viel mehr stellen simpel Dinge live aktualisieren können. Und das geht nun teilweise bis in die kleinsten Details: Ändert sich der Name einer Person, ändert sich die Beschriftung aller Links auf dieser Person bei allen Benutzern! Zudem ist es jetzt möglich, dem Server sehr einfach mitzuteilen, welche Informationen benötigt werden – auch das Senden von nur einzelnen geänderten Eigenschaften ist möglich und für die Zukunft vorgesehen – so spart man noch mehr Bandbreite.

Aber genug der Theorie – wie kannst Du nun damit herumspielen? Ganz einfach, wir erklären dir wie du lokal mit der Javascript-Konsole Daten verändern kannst. Keine Bange, beim Ausprobieren kannst du nichts kaputt machen – lokale Änderungen werden nicht zum Server gesendet. Zwei Funktionen regeln alles:

update_object(type, id, data, user)
Ändere Daten eines Objekts, das durch sein Model (type, z.B. 'comment') und id definiert wird. Wenn das Objekt aktuell nicht bekannt ist, wird es angelegt. type und id kannst du aus der oben genannten Variable objects herausfinden. data sollte ein Javascript-Objekt mit neuen Werten für Eigenschaften sein, die geändert werden sollen. user kannst du weglassen, du kannst aber auch ein Object im Format {'name':'Frank Nord'} angeben – an allen Stellen auf der Seite, an denen dieses Objekt nun erscheint oder verändert wird oder verschwindet (gefilterte Listen) wird erscheint nun kurz dieser Name.

remove_object(type, id, user)
Entferne ein Objekt komplett. Die Parameter sind die selben wie bei update_object, nur dass hier keine Daten übergeben werden. Das Objekt verschwindet hiermit an allen Stellen der Seite, an denen es Dargestellt wird sowie ebenfalls aus allen Listen.

Beispiele:
Gehe auf eine Personenseite, auf der Kommentare angezeigt werden. Such dir einen Kommentar heraus, sodass du seine id weißt. Jetzt stehen dir folgende Möglichkeiten offen (id jeweils ersetzen):

// Verändert den Text dieses Kommentars
update_object('comment', id, {'text': 'lalala'})

// Erstellt einen neuen Kommentar zu der Person des gewählten
update_object('comment', 42, {'text': 'Neuer Kommentar', 'person': objects.comment[id].person})

// Ändert die Person, auf den sich der Kommentar bezieht (verschwindet aus der Liste)
update_object('comment', id, {'person': {'id':0}})

// Entfernt diesen Kommentar und Zeigt an dass eine Person namens „Ich“ das war.
remove_object('comment', id, {'name': 'Ich'})

Fragen? Immer her damit! Viel Spaß beim Erkunden! :)