Eine JavaScript Template Engine nutzen

Problem

Sie wollen eine JavaScript Template Engine nutzen, um JSON-Daten anzuzeigen.

Lösung

Dieses Rezept ist eine Bücherliste. Sie holt sich Informationen über ein Buch von einem Skript auf Server-Seite und fügt es einer Liste von Büchern im Browser hinzu. Die Details zum Buch werden vom Server als JSON-String zurückgegeben. Dabei wird die Pure Templating Engine (verfügbar unter http://plugins.jquery.com/project/pure) genutzt, um die Daten zu formatieren und in die Website einzufügen:

<!DOCTYPE html>
<html><head>
    <title>jQuery-Kochbuch - 17.4 Eine Javascript Template Engine nutzen</title>
    <style>.hidden { display: none }</style>
    <script type="text/javascript" src="jquery-1.3.2.min.js"></script>
    <script type="text/javascript" src="pure.js"></script>
</head>

<body>
    <h1>17.4 - Eine JavaScript Template Engine nutzen</h1>
    <input type="button" id="add-book" value="Buch hinzufügen"></input>
    <input type="button" id="clear-list" value="Buchliste löschen"></input>
    <div id="book-list"></div>

Es gibt zwei Buttons. Einer holt sich die Buch-Details vom Server. Der andere löscht die lokal angezeigte Bücherliste. Diese Liste wird innerhalb eines <div>-Elements mit der id mit dem Wert book-list angezeigt. Diese Elemente sind sichtbar, wenn die Seite geladen wird:

     <div id="book-template" class="hidden book">
        <ul class="author-list"><li class="author"><span class="name"></span>
        </li></ul>
        <p class="title"></p>
        <p class="year"></p>
        <div class='book-footer'>
            <div class="rating-div">Bewertung: <span class="rating"></span></div>
            <div>Position: <span class="location"></span></div>
        </div>
    </div>
</body></html>

Das <div> mit der id mit dem Wert book-template besitzt die Klasse hidden. Dieses <div> wird nicht angezeigt. Es wird als Template für die Daten genutzt, die vom Server empfangen werden. Die Pure Templating Engine verbindet die Attribute in einer Datenstruktur mit HTML-Elementen, die die gleichen Klassen besitzen. Daher entspricht der Inhalt des Absatzelements mit der Klasse year dem Wert des Attributs year unserer Datenstruktur:

{
    "title": "Democracy and the Post-Totalitarian Experience",
    "author": [
        {
            "name": "Leszek Koczanowicz"
        },
        {
            "name": "Beth J. Singer"
        }
    ],
    "year": "2005",
    "rating": "3",
    "location": "Mandalay"
}

Dieser Code ist ein Beispiel für die JSON-Daten, die vom Server zurückgegeben werden. Die Attribute title, year, rating und location besitzen einzelne Werte und lassen sich direkt auf einzelne Elemente im HTML-Template abbilden. Um einen dieser Werte mehr als einmal anzuzeigen, muss man nur die entsprechende Klasse den zusätzlichen Elementen im Template zuweisen.

Das Attribut author enthält ein Array mit Objekten. Jedes Objekt besitzt ein einzelnes Attribut name. So lassen sich mehrere Autoren darstellen, mittels derer auch die Iterationsfähigkeiten der Template Engine gezeigt werden sollen. Das Template enthält ein einzelnes List-Element mit der Klasse author. Das List-Element enthält wiederum ein <span>-Element mit der Klasse name. Für Attribute innerhalb der Datenstruktur, die einen Array-Wert besitzen, wird für jedes Element im Array eine Instanz des entsprechenden HTML-Elements erzeugt. So lassen sich beliebig viele List-Elemente erzeugen:

(function($) {
    $('document').ready(function() {
        $('#add-book').data('id',1);

Ist das Dokument bereit, dann wird die jQuery-Funktion data() genutzt, um die aktuelle id des Buches zu speichern, das wir abfragen wollen. Diese id wird jedes Mal erhöht, wenn man die Informationen zu einem Buch abfragen will. Die Funktion data() erlaubt das Speichern beliebiger Daten in DOM-Elementen:

        $('#add-book').click(function() {
            var curId = $(this).data('id');
            $.getJSON('server.php', {id: +curId}, function(data) {
                if( data.none ) { return false; }
                var divId = 'book-' + curId;
                $('#book-list').append($('#book-template').clone().attr('id',divId));
                $('#'+divId).autoRender(data).removeClass('hidden');
                $('#add-book').data('id', curId + 1);
            });
            return false;
        });

Wird der Button »Buch hinzufügen« angeklickt, dann erzeugt der Code mit Hilfe der jQuery-Funktion getJSON() eine Anfrage an den Server. Der Template-Prozess beginnt mit dem Erzeugen einer Kopie des verborgenen <div> in unserem HTML-Code. Die id dieser Kopie muss geändert werden, bevor sie an die Bücherliste angefügt wird. Wenn man sie nicht ändert, wird ein DOM-Element mit einer nicht-eindeutigen id erzeugt. Dann wird die Funktion autoRender() aus dem Pure-Plugin mit den JSON-Daten als Argument aufgerufen. Damit wird das Template mit den angegebenen Daten gefüllt. Zum Schluss wird die Klasse hidden entfernt und die Buch-Details damit sichtbar gemacht:

        $('#clear-list').click(function() {
            $('#add-book').data('id',1);
            $('#book-list').empty();
            return false;
        });
    });
})(jQuery);

Die Funktion zum Löschen der Bücherliste ist ziemlich einfach. Das entsprechende <div>-Element wird geleert und der Buchzähler id auf 1 zurückgesetzt.

Diskussion

Es gibt zwei Vorteile durch die Verwendung einer JavaScript-basierten Templating Engine. Der eine ist die Möglichkeit, eine JSON-Datenstruktur in formatierten und strukturierten HTML-Code umzuwandeln, ohne selbst jedes Element der Datenstruktur anfassen zu müssen. Dazu wendet man eine Templating Engine auf die verschiedenen Datenelemente an, die man im Allgemeinen über Ajax-Aufrufe erhält – wie dieses Beispiel schön zeigt.

Der zweite Vorteil einer auf JavaScript basierenden Templating Engine ist das Erzeugen reiner HTML-Templates. Diese enthalten keine Bestandteile der Skriptsprache, die normalerweise genutzt werden, um die zu verarbeitenden Daten zu markieren und zum Beispiel Iterationen zu ermöglichen. Es ist schwer, diesen Vorteil nutzen zu können, wenn man die Templating Engine im Browser verwendet. Die negativen Auswirkungen auf die Attraktivität der Site für Suchmaschinen hält die meisten Entwickler von diesem Vorgehen ab. Aber jQuery und die Pure Templating Engine können auch in JavaScript-Umgebungen auf Server-Seite genutzt werden. Jaxer, Rhino und SpiderMonkey sind ebenfalls bekannt Beispiele.

JQuery Kochbuch
titlepage.xhtml
part0000.html
part0001.html
part0002_split_000.html
part0002_split_001.html
part0002_split_002.html
part0003_split_000.html
part0003_split_001.html
part0003_split_002.html
part0003_split_003.html
part0003_split_004.html
part0003_split_005.html
part0003_split_006.html
part0003_split_007.html
part0004_split_000.html
part0004_split_001.html
part0004_split_002.html
part0004_split_003.html
part0004_split_004.html
part0004_split_005.html
part0004_split_006.html
part0004_split_007.html
part0004_split_008.html
part0004_split_009.html
part0004_split_010.html
part0004_split_011.html
part0004_split_012.html
part0004_split_013.html
part0004_split_014.html
part0004_split_015.html
part0004_split_016.html
part0004_split_017.html
part0004_split_018.html
part0005_split_000.html
part0005_split_001.html
part0005_split_002.html
part0005_split_003.html
part0005_split_004.html
part0005_split_005.html
part0005_split_006.html
part0005_split_007.html
part0005_split_008.html
part0005_split_009.html
part0005_split_010.html
part0005_split_011.html
part0005_split_012.html
part0005_split_013.html
part0006_split_000.html
part0006_split_001.html
part0006_split_002.html
part0006_split_003.html
part0006_split_004.html
part0006_split_005.html
part0006_split_006.html
part0006_split_007.html
part0006_split_008.html
part0006_split_009.html
part0006_split_010.html
part0007_split_000.html
part0007_split_001.html
part0007_split_002.html
part0007_split_003.html
part0007_split_004.html
part0007_split_005.html
part0007_split_006.html
part0007_split_007.html
part0007_split_008.html
part0007_split_009.html
part0007_split_010.html
part0007_split_011.html
part0008_split_000.html
part0008_split_001.html
part0008_split_002.html
part0008_split_003.html
part0008_split_004.html
part0008_split_005.html
part0008_split_006.html
part0008_split_007.html
part0008_split_008.html
part0008_split_009.html
part0008_split_010.html
part0008_split_011.html
part0008_split_012.html
part0008_split_013.html
part0008_split_014.html
part0008_split_015.html
part0008_split_016.html
part0008_split_017.html
part0008_split_018.html
part0008_split_019.html
part0008_split_020.html
part0008_split_021.html
part0008_split_022.html
part0009_split_000.html
part0009_split_001.html
part0009_split_002.html
part0009_split_003.html
part0009_split_004.html
part0009_split_005.html
part0009_split_006.html
part0009_split_007.html
part0009_split_008.html
part0009_split_009.html
part0009_split_010.html
part0010_split_000.html
part0010_split_001.html
part0010_split_002.html
part0010_split_003.html
part0010_split_004.html
part0010_split_005.html
part0010_split_006.html
part0010_split_007.html
part0010_split_008.html
part0010_split_009.html
part0010_split_010.html
part0010_split_011.html
part0011_split_000.html
part0011_split_001.html
part0011_split_002.html
part0011_split_003.html
part0011_split_004.html
part0011_split_005.html
part0011_split_006.html
part0011_split_007.html
part0011_split_008.html
part0011_split_009.html
part0011_split_010.html
part0011_split_011.html
part0012_split_000.html
part0012_split_001.html
part0012_split_002.html
part0012_split_003.html
part0012_split_004.html
part0012_split_005.html
part0012_split_006.html
part0012_split_007.html
part0012_split_008.html
part0013_split_000.html
part0013_split_001.html
part0013_split_002.html
part0013_split_003.html
part0013_split_004.html
part0013_split_005.html
part0013_split_006.html
part0013_split_007.html
part0013_split_008.html
part0013_split_009.html
part0013_split_010.html
part0013_split_011.html
part0013_split_012.html
part0014_split_000.html
part0014_split_001.html
part0014_split_002.html
part0014_split_003.html
part0014_split_004.html
part0014_split_005.html
part0014_split_006.html
part0014_split_007.html
part0014_split_008.html
part0014_split_009.html
part0014_split_010.html
part0014_split_011.html
part0015_split_000.html
part0015_split_001.html
part0015_split_002.html
part0015_split_003.html
part0015_split_004.html
part0015_split_005.html
part0015_split_006.html
part0015_split_007.html
part0015_split_008.html
part0015_split_009.html
part0015_split_010.html
part0016_split_000.html
part0016_split_001.html
part0016_split_002.html
part0016_split_003.html
part0016_split_004.html
part0016_split_005.html
part0016_split_006.html
part0016_split_007.html
part0016_split_008.html
part0016_split_009.html
part0017_split_000.html
part0017_split_001.html
part0017_split_002.html
part0017_split_003.html
part0017_split_004.html
part0017_split_005.html
part0017_split_006.html
part0017_split_007.html
part0017_split_008.html
part0017_split_009.html
part0017_split_010.html
part0017_split_011.html
part0018_split_000.html
part0018_split_001.html
part0018_split_002.html
part0018_split_003.html
part0018_split_004.html
part0018_split_005.html
part0018_split_006.html
part0019_split_000.html
part0019_split_001.html
part0019_split_002.html
part0019_split_003.html
part0019_split_004.html
part0019_split_005.html
part0019_split_006.html
part0019_split_007.html
part0019_split_008.html
part0019_split_009.html
part0019_split_010.html
part0020_split_000.html
part0020_split_001.html
part0020_split_002.html
part0020_split_003.html
part0020_split_004.html
part0020_split_005.html
part0020_split_006.html
part0020_split_007.html
part0020_split_008.html
part0021_split_000.html
part0021_split_001.html
part0021_split_002.html
part0021_split_003.html
part0021_split_004.html
part0021_split_005.html
part0021_split_006.html
part0021_split_007.html
part0021_split_008.html
part0021_split_009.html
part0022.html
part0023.html
part0024.html
part0025.html