Zurechtgezwirbelt: Mit Mustache zur dynamischen Single-Page-App

Dynamisches Erzeugen von HTML ist wesentlich für Webanwendungen. Ein gezielter Einsatz des passenden Werkzeugs erspart das Verwenden umfangreicher Frameworks.

Lesezeit: 6 Min.
In Pocket speichern
vorlesen Druckansicht Kommentare lesen 23 Beiträge

(Bild: jakkaje879/Shutterstock.com)

Von
  • Ulrich Hilger
Inhaltsverzeichnis

Single-Page-Webanwendungen (SPA) bedeuten einen fortwährenden Neuaufbau von Teilen der Bedienoberfläche. Jede Änderung entspricht dabei dem dynamischen Erzeugen von HTML-Code im Hintergrund und dessen Einbau in die im Browser dargestellte HTML-Seite zur Laufzeit. Für diesen Mechanismus gibt es schlanke und einfach zu verwendende Werkzeuge. Dieser Beitrag zeigt das beispielhaft anhand des Web-Template-Systems Mustache.

In einer HTML-Seite sind Inhalte mit der Auszeichnung von Struktur- und Gestaltungsinformationen vermischt. Ein Vorlagenmechanismus führt zur Trennung von Inhalten und Auszeichnungen, die erst zur Laufzeit einer App zusammengeführt werden. Dafür wirken verschiedene Elemente zusammen:

  • Inhalte, die die App anzeigen soll und die üblicherweise im JSON-Format vorliegen, ruft sie zur Laufzeit vom Server ab.
  • Eine Vorlage enthält den HTML-Code mit der Beschreibung, wie Inhalte erscheinen sollen. An den Stellen, an denen die in HTML beschriebenen Inhalte aus den JSON-Daten vom Server vorgesehen sind, befinden sich Platzhalter.
  • Das Vorlagenwerkzeug erkennt sich wiederholende Strukturen im Inhalt. Es sorgt automatisch für das Erstellen wiederkehrender HTML-Muster wie der stets gleichen HTML-Ausdrücke für die zahlreichen gleichartigen Elemente einer Liste.
  • JavaScript-Funktionen zum Verwenden des Vorlagenwerkzeugs erstellen sowohl Inhaltseinträge an den von den Platzhaltern bezeichneten Stellen in der Vorlage als auch HTML-Code. Letzterer enthält Auszeichnungen und darstellungsbereite Inhalte.

Viele Schritte, die mit dem Anwendungsmuster einhergehen, lassen sich als kleine Skriptbibliothek vorwegnehmen, so dass fertige Hilfsfunktionen die Prozedur fast vollständig übernehmen können. Was für eine eigene SPA dann noch zu tun ist, erfordert nur wenige Zeilen an zusätzlichem Code.

Die folgenden Abschnitte beschreiben die Implementierung des Anwendungsmusters. Darauf folgt eine Übersicht der Funktionen, die sich unverändert übernehmen und wiederverwenden lassen.

Das Einsetzen des Anwendungsmusters erfordert eine HTML-Seite mit folgendem Inhalt:

<!DOCTYPE html>
<html>
  <head>
    <!-- Kopfdaten nach Bedarf -->
    <link rel="stylesheet" type="text/css" href="app.css">
  </head>
  <body>
    <!-- weiterer HTML-Inhalt nach Bedarf -->
    <div class="zentraler-inhalt">
      <!-- hier wird zur Laufzeit Inhalt dynamisch hinzugefügt -->
    </div>
    <!-- weiterer HTML-Inhalt nach Bedarf -->

    <!-- Skripte -->
    <script src="js/mustache/mustache.min.js"></script>
    <script src="js/app.js"></script>
    <script>
      document.addEventListener('DOMContentLoaded', function () {
        meine_spa_initialisieren(); // irgendwo in app.js
      });
    </script>
  </body>
</html>

Zusätzlich zum Einbinden des Skripts von Mustache bindet app.js das Skript der eigenen App ein und ergänzt darin die nachfolgend beschriebenen Funktionen. Im Beispiel ist der Bereich .zentraler-inhalt veränderlich gemeint. Dem tragen verschiedene Vorlagen Rechnung, die mit Inhalt gefüllt zur Laufzeit einsetzbar sind.

Zum Abrufen von Inhalten dient gewöhnlich ein einfaches HTTP GET, etwa zur Abfrage des Inhalts eines Ordners mit Audiodateien:

HTTP GET \
  http://mein-server:9191/tango/media/Musik/A/ACDC/Highway-To-Hell/

Aus einer App heraus sieht das Ausführen der Ausgabe wie folgt aus:

function inhalt_abrufen() {
  var serverUrl = 'http://mein-server:9191/tango';
  var serviceUrl = '/media/Musik/A/ACDC/Highway-To-Hell/';
  var getUrl = serverUrl + serviceUrl;
  http_call('GET', getUrl, '', function(antwort) {
    // hier die Antwort verarbeiten
  });
}

Dazu nutzt der Code die Hilfsfunktion http_call, die in der wiederverwendbaren Bibliothek weiter unten enthalten ist. Der Server antwortet darauf mit dem Inhalt des Ordners als JSON-Struktur:

{
  "Medialiste": [{
      "name": "01-Highway-to-hell.mp3",
      "typ": "audio",
      "interpret": "ACDC",
      "titelAnzName": "Highway to hell",
      "album": "Highway To Hell"
    }, {
      "name": "02-Girls-got-rhythm.mp3",
      "typ": "audio",
      "interpret": "ACDC",
      "titelAnzName": "Girls got rhythm",
      "album": "Highway To Hell"
    },

    // weitere Angaben hier

    {
      "name": "10-Night-prowler.mp3",
      "typ": "audio",
      "interpret": "ACDC",
      "titelAnzName": "Night prowler",
      "album": "Highway To Hell"
    }]
}

Der nächste Schritt verwandelt diesen Inhalt mithilfe von Mustache in HTML:

var vorlage = 'vorlagen/titel-liste.txt';
html_erzeugen(vorlage, JSON.parse(antwort), function(html) {
  // hier das HTML in die HTML-Seite schreiben
  // wie weiter unten beschrieben
});

Hierbei kommt erneut Code aus der wiederverwendbaren Bibliothek zum Einsatz. Die Funktion html_erzeugen liest die Vorlage vorlagen/titel-liste.txt aus dem Vorlagen-Cache und weist Mustache an, den JSON-Inhalt vom Server als HTML zu erzeugen. Wenn sich die gewünschte Vorlage noch nicht im Cache befindet, ist zunächst das Laden vom Server nötig.

Das asynchrone Laden der Vorlage bedingt, dass eine Callback-Funktion den fertigen HTML-Code verarbeitet. Darauf folgt die Übergabe an die Funktion html_erzeugen und die Ausführung, sobald die Vorlage im Cache ist.

Die Vorlage vorlagen/titel-liste.txt enthält dabei folgenden, mit Platzhaltern gemischten HTML-Code:

<div class='titel-liste-behaelter'>
  <ul class='titel-liste'>
    {{#Medialiste}}
    <li class='entity-eintrag entity-typ-{{typ}} noselect'
        typ='{{typ}}'
        interpret='{{interpret}}'
        album='{{album}}'
        titelAnzName='{{titelAnzName}}'
        dateiName='{{name}}'>{{titelAnzName}}</li>
    {{/Medialiste}}
  </ul>
</div>

Die Namen der Platzhalter entsprechen den Feldnamen der JSON-Struktur vom Server. Auf diese Weise kann Mustache erkennen, welche Teile des JSON-Inhalts an welchen Stellen im HTML-Code erscheinen müssen. Aus der Vorlage und dem JSON-Inhalt macht Mustache schließlich folgenden HTML-Block:

<div class='titel-liste-behaelter'>
  <ul class='titel-liste'>
    <li class='entity-eintrag entity-typ-audio noselect'
        typ='audio' interpret='ACDC' album='Highway To Hell'
        titelAnzName='Highway to hell' 
        dateiName='01-Highway-to-hell.mp3'>
          Highway to hell
    </li>
    <li class='entity-eintrag entity-typ-audio noselect'
        typ='audio' interpret='ACDC' album='Highway To Hell'
        titelAnzName='Girls got rhythm' 
        dateiName='02-Girls-got-rhythm.mp3'>
          Girls got rhythm
    </li>

    <!-- weitere Titel -->

    <li class='entity-eintrag entity-typ-audio noselect'
        typ='audio' interpret='ACDC' album='Highway To Hell'
        titelAnzName='Night prowler' 
        dateiName='10-Night-prowler.mp3'>
          Night prowler
    </li>
  </ul>
</div>

Am HTML-Ergebnis ist ersichtlich, wie viel Text sich je Datenelement wiederholt. Ein typischer Nutzen von Vorlagen ist, dass eine solche Struktur in der Vorlage nur einmal stellvertretend für den jeweiligen Typ von Inhalt zu formulieren ist. Den restlichen HTML-Code erstellt das Vorlagenwerkzeug dynamisch am jeweiligen Inhalt orientiert.

Danach erfolgt das Hinzufügen des resultierenden HTML-Codes zur HTML-Seite:

document.querySelector(".zentraler-inhalt").innerHTML = html;

Somit hat Mustache Schritt für Schritt einen beliebigen Teilinhalt vom Server abgerufen, dynamisch in HTML umgewandelt und der HTML-Seite einer Single-Page-Webanwendung hinzugefügt. Das erweckt den Anschein einer umfänglichen Prozedur, was aber nur an der recht eingehenden Betrachtung liegt. Tatsächlich führen der Einsatz von Mustache sowie der beiden Hilfsfunktionen http_call und html_erzeugen zu einer überschaubaren Vorgehensweise. Zum Beleg folgt der Code nochmals in der Zusammenfassung.

function audio_ordner_klick() {
  // Code, z. B. zur Ermittlung des gewuenschten Inhalts
  // von 'service' koennte hier stehen. Nachfolgend
  // stattdessen hart codiert.
  var service = '/media/Musik/A/ACDC/Highway-To-Hell/';
  var url = 'http://mein-server:9191/audio-app' + service;
  inhalt_abrufen_und_zeigen(url, 'vorlagen/titel-liste.txt');
}

function inhalt_abrufen_und_zeigen(url, vorlage) {
  http_call('GET', url, '', function(antwort) {
    html_erzeugen(vorlage, JSON.parse(antwort), function(html) {
      document.querySelector(".zentraler-inhalt").innerHTML = html;
    });
  });
}

Dieser Codeblock in einer Single-Page-Webanwendung genügt zum dynamischen Erzeugen von HTML. Um den Rest kümmern sich Mustache und die erwähnten Hilfsfunktionen.