Go Templates + jQuery + Bootstrap

jQuery Funktionen und Go

Bei der gemeinsamen Verwendung von Go Templates und jQuery lassen sich mit relativ wenigen der zahlreichen jQuery Funktionen Single Page Applikationen effektiv und gut strukturiert aufbauen. Das Ziel dabei ist, möglichst viel der Funktionalität in Go zu realisieren und möglichst wenig JavaScript zu verwenden. Bootstrap liefert zusätzlich die notwendigen Hilfsmittel, um gut strukturierte, responsive Layouts zu erzielen, die von UHD Bildschirmen bis zu kleinen Smartphones korrekt angezeigt werden können. Besonders hilfreich sind bei der Entwicklung folgende jQuery Funktionen:

.load( url [, data ] [, complete ] )
lädt Daten vom Server und platziert das gelieferte HTML in das ausgewählte DOM Element. Mit einem Go Template lässt sich sehr einfach das HTML Fragment erzeugen, das auf der Webseite in ein bestimmtes Element geladen wird. Typischerweise ist das Ziel ein <div> Element.
Damit lassen sich z.B. Formulare im Go Template mit den Werten des aktuellen Datensatzes füllen und in den Browser laden. JavaScript Code, der die Felder aus JSON Responsedaten füllt, kann vollständig entfallen. Allerdings müssen dabei mehr Daten übertragen werden als bei einem reinen AJAX Response.
$.ready( handler )
ruft eine Callback Funktion auf (handler), wenn das DOM vollständig aufgebaut ist. Das funktioniert auch bei der Übertragung von HTML Fragmenten. Damit können die neu geladenen Seitenteile in einfacher Weise initialisiert werde.
.serialize()
schreibt die Inhalte von Formularelementen in einen String, der an den Server übertragen werden kann.
$.post( url [, data ] [, success ] [, dataType ] )
sendet Daten zum Server mittels HTTP POST Request und ruft eine Callback-Funktion (success) mit den Response-Daten auf. Damit lassen sich sehr gut serialisierte Formulardaten an den Server senden. In der Callback Funktion kann man dann auf das Ergebnis der Verarbeitung der Formulardaten reagieren. Bei Erfolg kann z.B. der nächste Datensatz mit .load( … ) geladen oder es können Fehler angezeigt werden.
.attr(), .val()
Mit diesen Funktionen werden Attribute oder Werte von DOM Elementen geändert. Die Ergebnisse eines Response nach einem .post() können damit einfach zugeordnet werden.
jQuery Selektoren
ermöglichen einen einfachen Zugriff auf die DOM Elemente des HTML Dokuments. Die Selektion erfolgt z.B. über das id Attribut, den Typ, den Namen usw., praktisch über alle CSS3 Selektoren. Es können Elemente entlang der Pfade im DOM ausgewählt werden, z.B. alle <input> Elemente innerhalb einer <div> mit bestimmter id. Damit lässt sich sehr gut bestimmen, wohin HTML Fragmente geladen oder welche Elementwerte oder -attribute verändert werden sollen. Serverseitig können Go Tags verwendet werden, um Felder von structs zu bestimmen Elementen im DOM über deren id Attribute zuzuordnen. Das verringert die Redundanz bei der Zuordnung von Go Datenelementen zu HTML Formelementen. Das könnte soweit ausgebaut werden, dass die IDs grundsätzlich über die Tag Annotationen bestimmt werden. Damit würde das Single Source Konzept realisiert.

Implementierungs – Muster

Bei der Implementierung des Beispiels haben sich einige Muster herausgebildet, die die oben angegebenen jQuery Funktionen verwenden. Sie werden an verschiedenen Stellen angewendet und vereinfachen das Zusammenspiel der verschiedenen Technologien sehr.

Initialisierung von Seitenfragmenten

Go Templates können in andere Templates eingebettet oder auch dynamisch über JavaScript und jQuery nachgeladen werden. Mit der $.ready() Funktion von jQuery lassen sich dann notwendige Initialisierungen durchführen. Diese Funktion wird jedesmal aufgerufen, wenn das Seitenfragment (nach-)geladen wird. Im Beispielprojekt werden jedesmal Forms neu geladen, wenn ein neuer Datensatz im Suchfeld ausgewählt wird.

49
$.ready() wird aufgerufen, wenn das DOM vollständig geladen ist. Als Parameter wird eine anonyme Handler Funktion übergeben, in der die Initialisierungen erfolgen.
51
An das Element (ein Button) mit der ID ‚change-enrol‘ wird eine Click Handler Funktion gebunden. Die Map vor dem function Parameter wird an die Handler Funktion übergeben.
53
Das jQuery validator Plugin muss nach jedem neuen Laden der Form aktualisiert werden.

Zur Veranschaulichung der Parameterübergabe hier die Click Handler Funktion enableFormFields:

Form Submissions

Forms werden nicht einfach mit dem Submit Button abgeschickt, weil dies einen neuen Seitenaufbau bewirken würde. Stattdessen wird über eine JavaScript Funktion folgender grundsätzlicher Ablauf angestoßen:
Submit Button click → Form Daten serialisieren
→ Daten an Server senden und Callback für Response setzen
→ Server validiert die Daten und bearbeitet sie weiter
→ Wenn alles ok ist, leeren Response an Client, sonst Response mit Fehlerdaten
→ Callback im Client zeigt nächsten Datensatz an oder bearbeitet Fehlerdaten
→ Fertig

Im Beispielprogramm sieht der Ablauf z.B. bei der Einschreibung eines Bewerbers so aus:

typischer Form Submit Ablauf
typischer Form Submit Ablauf
  1. Form Action an die entsprechende JavaScript Funktion binden
  2. Die action Handler Function serialisiert die Form und ruft die Funktion zum Posten auf
  3. Funktion postSubmissionAndReload wird für Form Posts aus verschiedenen Tabs genutzt.

    118
    Zum Senden der Daten an den Server wird die jQuery Funktion $.post() verwendet. Als optionalen Parameter bekommt $.post() hier eine Funktion, die asynchron als Callback aufgerufen wird, wenn der Response eintrifft.
    119, 125-128
    Ein Fehler, der weiter verarbeitet werden kann, wird als Map erwartet. Für alle Keys wird mergeVal aufgerufen.
    123, 124
    Fehler oder Datensatz gelöscht
    131
    Wenn der Submit erfolgreich war, wird die Form mit dem nächsten Datensatz geladen.
  4. Auf dem Server werden die übermittelten Daten in eine passende Datenstruktur übernommen und weiter verarbeitet. Ganz wesentlich sind dabei verschiedene Plausibilitätsprüfungen, um Fehlbedienungen und Exploits zu vermeiden (siehe Request Filter mit Alice).
    1. Im ersten Schritt sind das die Request Filter. Danach überprüft der Handler, ob die Request Methode erwartet wird und ruft nur im Erfolgsfall die Datenübernahme auf.
    2. In saveApplicantSubmission werden weitere mögliche Fehler abgefangen: Lassen sich die übermittelten Daten parsen, lassen sich die alten Daten aus dem Session Store wiederherstellen. Wenn alles gut geht, können die Daten mit der Funktion  setApplicantData( ... )  übernommen werden.
    3.  In setApplicantData( ... ) werden die alten Daten mit neuen Werten aus dem Request überschrieben.

      Die interessante Methode ist dabei setIfPosted( … ), die Go Reflection benutzt, um auf Felder der Datenstruktur zuzugreifen.

      285
      Als Parameter werden eine Referenz auf das zu ändernde Feld der struct ( target interface{} ), der Schlüssel des übertragenen Wertes in den PostFormValues ( key string ) und der Http Request ( r *http.Request ) übergeben.
      286, 288
      Zuerst wird getestet, ob zum gegebenen key ein Form-Wert vorhanden ist. Die Werte, die mit r.PostFormValue(key)  aus dem HTTP Request geholt werden, müssen unbedingt noch mit def Funktion html.EscapeString( ... )  abgesichert werden, um die Injektion von (bösartigem) Code in die Datenbank zu verhindern.
      290-291
      Der Parameter target, der durch den entsprechenden Wert aus dem Request überschrieben werden soll, ist als leeres interface{}, d.h. als allgemeinster Objekttyp von Go definiert.
      Mit  settable := reflect.ValueOf(target).Elem() wird die Referenz auf den zugrundeliegenden aktuellen Parameter geholt. Damit er verändert werden kann, muss beim Aufruf der Funktion target als Pointer übergeben werden, siehe im vorigen Codebeispiel. Nur dann liefert die Methode CanSet() true.
      295, 297, 299, 301, 303
      Innerhalb des type switch wird dem Parameter target indirekt über die Referenzvariable targetref der (ggf. konvertierte) Wert aus dem Request zugewiesen. Die Funktion deckt nur die im Beispiel verwendeten Typen ab. Für weitere Datentypen muss der type switch erweitert werden.