Erste Schritte mit HTL

HTML Template Language (HTL) ist das bevorzugte und empfohlene Server-seitige Vorlagensystem für HTML in Adobe Experience Manager. Wie bei allen Server-seitigen HTML-Templating-Systemen definiert eine HTL-Datei die an den Browser gesendete Ausgabe, indem sie den HTML-Code selbst, einige grundlegende Darstellungslogiken sowie Variablen angibt, die zur Laufzeit ausgewertet werden.

Dieses Dokument gibt einen Überblick über den Zweck der HTL sowie eine Einführung in die grundlegenden Konzepte und Konstrukte der Sprache.

TIPP

Dieses Dokument stellt den Zweck der HTL und einen Überblick über ihre grundlegende Struktur und Konzepte vor. Wenn Sie Fragen zur spezifischen Syntax haben, sehen Sie bitte in der HTL-Spezifikation nach.

HTL-Ebenen

HTL, wie es in AEM verwendet wird, kann durch eine Reihe von Ebenen definiert werden.

  1. HTL-Spezifikation – HTL ist eine quelloffene, plattformunabhängige Spezifikation, die von jeder Person frei implementiert werden kann.
  2. Sling HTL Scripting Engine – Das Sling-Projekt hat die Referenzimplementierung von HTL erstellt, die von AEM verwendet wird.
  3. AEM-Erweiterungen – AEM baut auf der Sling HTL Scripting Engine auf, um Entwickelnden praktische, AEM-spezifische Funktionen zu bieten.

Diese HTL-Dokumentation konzentriert sich auf die Verwendung von HTL zur Entwicklung von AEM-Lösungen. Als solche berührt sie alle drei Ebenen und verknüpft bei Bedarf externe Ressourcen.

Grundsätzliche HTL-Konzepte

Die HTML-Vorlagensprache verwendet eine Ausdruckssprache zum Einfügen von Inhaltsteilen in das gerenderte Markup und über HTML5-Datenattribute zum Definieren von Anweisungen über Markup-Blöcke (wie Bedingungen oder Wiederholungen). Wenn HTL in Java-Servlets kompiliert wird, werden die Ausdrücke und die HTL-Datenattribute komplett Server-seitig ausgewertet. Zudem ist in der HTML-Ausgabe nichts mehr davon sichtbar.

TIPP

Zum Ausführen der meisten auf dieser Seite bereitgestellten Beispiele kann eine als Read Eval Print Loop (REPL) bezeichnete Live-Ausführungsumgebung verwendet werden.

Blöcke und Ausdrücke

Hier finden Sie ein erstes Beispiel, das in der Datei template.html enthalten sein könnte:

<h1 data-sly-test="${properties.jcr:title}">
    ${properties.jcr:title}
</h1>

Es lassen sich zwei verschiedene Arten von Syntaxen unterscheiden:

  • Block-Anweisungen – Um das <h1>-Element bedingt anzuzeigen, wird ein data-sly-test-HTML5-Datenattribut verwendet. HTL bietet mehrere solcher Attribute, die es ermöglichen, jedem HTML-Element ein bestimmtes Verhalten zuzuordnen, und die alle mit dem Präfix data-sly versehen sind.
  • Ausdruckssprache – HTL-Ausdrücke werden durch die Zeichen ${ und } abgegrenzt. Diese Ausdrücke werden zur Laufzeit ausgewertet, und ihr Wert wird in den ausgehenden HTML-Stream eingeschleust.

Siehe die HTL-Spezifikation für Details zu beiden Syntaxen.

Das SLY-Element

Ein zentrales Konzept von HTL ist die Möglichkeit, vorhandene HTML-Elemente wiederzuverwenden, um Blockanweisungen zu definieren, ohne dass zusätzliche Begrenzungszeichen eingefügt werden müssen, die festlegen, wo die Anweisung beginnt und endet. Diese unauffällige Annotation des Markups zur Umwandlung einer statischen HTML in eine funktionierende dynamische Vorlage ermöglicht es, dass die Gültigkeit des HTML-Codes nicht gebrochen wird, wodurch weiterhin die ordnungsgemäße Anzeige erfolgt, sogar als statische Dateien.

Manchmal ist jedoch möglicherweise kein Element an der exakten Position vorhanden, an der eine Blockanweisung eingefügt werden muss. Für solche Fälle ist es möglich, ein spezielles sly-Element einzufügen, das automatisch aus der Ausgabe entfernt wird, während die angehängten Blockanweisungen ausgeführt werden und ihr Inhalt entsprechend angezeigt wird.

Im folgenden Beispiel…

<sly data-sly-test="${properties.jcr:title && properties.jcr:description}">
    <h1>${properties.jcr:title}</h1>
    <p>${properties.jcr:description}</p>
</sly>

… wird etwas wie das folgende HTML ausgegeben, aber nur, wenn sowohl eine jcr:title- als auch eine jcr:description-Eigenschaft definiert sind und keine der beiden leer ist:

<h1>MY TITLE</h1>
<p>MY DESCRIPTION</p>

Zu beachten ist, dass das Element sly nur dann verwendet werden sollte, wenn kein vorhandenes Element mit der Blockanweisung versehen werden konnte. Dies liegt daran, dass sly-Elemente den Wert der Sprache mindern, der darin besteht, das statische HTML nicht zu verändern, wenn es dynamisch gemacht wird.

Wäre das vorherige Beispiel beispielsweise in ein div-Element eingeschlossen worden, dann wäre das hinzugefügte sly-Element missbräuchlich:

<div>
    <sly data-sly-test="${properties.jcr:title && properties.jcr:description}">
        <h1>${properties.jcr:title}</h1>
        <p>${properties.jcr:description}</p>
    </sly>
</div>

und das Element div hätte mit der Bedingung versehen werden können:

<div data-sly-test="${properties.jcr:title && properties.jcr:description}">
    <h1>${properties.jcr:title}</h1>
    <p>${properties.jcr:description}</p>
</div>

HTL-Kommentare

Das folgende Beispiel zeigt einen HTL-Kommentar in der ersten Zeile und einen HTML-Kommentar in der zweiten Zeile.

<!--/* An HTL Comment */-->
<!-- An HTML Comment -->

HTL-Kommentare sind HTML-Kommentare mit einer zusätzlichen JavaScript-artigen Syntax. Der gesamte HTL-Kommentar und alles darin Enthaltene werden vollständig durch den Prozessor ignoriert und aus der Ausgabe entfernt.

Die Inhalte der HTML-Standardkommentare werden jedoch weitergegeben und die Ausdrücke im Kommentar werden ausgewertet.

HTML-Kommentare dürfen keine HTL-Kommentare enthalten und umgekehrt.

Spezielle Kontexte

Um HTL bestmöglich zu verwenden, ist es wichtig, die Konsequenzen zu verstehen, die sich daraus ergeben, dass sie auf der HTML-Syntax basiert.

Weitere Einzelheiten finden Sie im Abschnitt Anzeigekontext der HTL-Spezifikation.

Element- und Attributnamen

Ausdrücke können nur in HTML-Text oder Attributwerte eingefügt werden, nicht aber in Element- oder Attributnamen, sonst wäre es kein gültiges HTML mehr. Zum dynamischen Festlegen der Elementnamen kann die Anweisung data-sly-element für die gewünschten Elemente verwendet werden und zum dynamischen Festlegen der Attributnamen oder sogar zum Festlegen von mehreren Attributen gleichzeitig, die Anweisung data-sly-attribute.

<h1 data-sly-element="${myElementName}" data-sly-attribute="${myAttributeMap}">...</h1>

Kontexte ohne Blockanweisungen

Da HTL Datenattribute zum Definieren von Blockanweisungen verwendet, ist es nicht möglich, solche Blockanweisungen in den folgenden Kontexten zu definieren, und es können dort nur Ausdrücke verwendet werden:

  • HTML-Kommentare
  • Skriptelemente
  • Style-Elemente

Der Grund besteht darin, dass der Inhalt dieser Kontexte Text ist und keine HTML, wobei darin enthaltene HTML-Elemente als einfache Zeichendaten erkannt werden. Ohne echte HTML-Elemente ist die Ausführung von data-sly-Attributen demnach nicht möglich.

Dies mag nach einer erheblichen Einschränkung klingen, ist jedoch erwünscht, da die HTML Template Language nicht dazu missbraucht werden sollte, Ausgaben zu erzeugen, die nicht HTML sind. Im Abschnitt Anwendungs-API für den Zugriff auf die Logik unten wird erläutert, wie zusätzliche Logik über die Vorlage abgerufen werden kann, die verwendet werden kann, wenn die komplexe Ausgabe für diese Kontexte vorbereitet werden muss. Zum Beispiel kann die Logik der Komponente zum einfachen Senden von Daten vom Back-End- zum Front-End-Skript eine JSON-Zeichenfolge generieren, die anschließend in einem Datenattribut mit einem einfachen HTL-Ausdruck platziert werden kann.

Im folgenden Beispiel wird das Verhalten für HTML-Kommentare veranschaulicht, jedoch lässt sich in Skript- oder Style-Elementen dasselbe Verhalten beobachten:

<!--
    The title is: ${properties.jcr:title}
    <h1 data-sly-test="${properties.jcr:title}">${properties.jcr:title}</h1>
-->

gibt etwas wie die folgende HTML aus:

<!--
    The title is: MY TITLE
    <h1 data-sly-test="MY TITLE">MY TITLE</h1>
-->

Explizite Kontexte erforderlich

Wie im unten folgenden Abschnitt Automatische kontextsensitive Maskierung erläutert, besteht ein Ziel der HTL darin, das Risiko von Sicherheitslücken beim Cross-Site-Scripting (XSS) zu reduzieren, indem automatisch auf alle Ausdrücke die kontextsensitive Maskierung angewendet wird. Auch wenn HTL automatisch den Kontext der im HTML-Markup platzierten Ausdrücke erkennen kann, analysiert sie nicht die Syntax der Inline-JavaScript oder CSS und verlässt sich daher darauf, dass der Entwickler explizit angibt, welcher exakte Kontext auf solche Ausdrücke anzuwenden ist.

Da die Nichtanwendung der richtigen Maskierung zu XSS-Sicherheitslücken führt, entfernt HTL daher die Ausgabe sämtlicher in Skript- und Style-Kontexten befindlichen Ausdrücke, sofern der Kontext nicht deklariert wurde.

Im Folgenden finden Sie ein Beispiel hinsichtlich der Festlegung des Kontexts für die in Skripts und Styles platzierten Ausdrücke:

<script> var trackingID = "${myTrackingID @ context='scriptString'}"; </script>
<style> a { font-family: "${myFont @ context='styleString'}"; } </style>

Weitere Einzelheiten zur Steuerung des Escapings finden Sie im Abschnitt Anzeigekontext der Ausdruckssprache der HTL-Spezifikationen.

Allgemeine HTL-Funktionen

In diesem Abschnitt werden die allgemeinen Funktionen der HTML-Vorlagensprache kurz behandelt.

Anwendungs-API für den Zugriff auf die Logik

Mit der Java-Anwendungs-API der HTML-Vorlagensprache (HTL) wird es einer HTL-Datei ermöglicht, in einer benutzerdefinierten Java-Klasse über data-sly-use auf die Hilfsmethoden zuzugreifen. Dadurch kann die gesamte komplexe Geschäftslogik im Java-Code verschachtelt werden, während der HTL-Code nur die direkte Markup-Produktion verarbeiten muss.

Siehe das Dokument HTL-Java-Anwendungs-API für weitere Details.

Automatische kontextsensitive Maskierung

Siehe folgendes Beispiel:

<p data-sly-use.logic="logic.js">
    <a href="${logic.link}" title="${logic.title}">
        ${logic.text}
    </a>
</p>

In den meisten Vorlagensprachen führt dieses Beispiel potenziell zu Sicherheitslücken beim Cross-Site-Scripting (XSS), da, selbst wenn alle Variablen automatisch HTML-maskiert sind, das Attribut href weiterhin speziell URL-maskiert werden muss. Dieses Versäumnis ist einer der häufigsten Fehler, da es leicht vergessen werden kann und nur schwer automatisiert zu erkennen ist.

Zur Abhilfe maskiert die HTML-Vorlagensprache jede Variable automatisch gemäß dem Kontext, in dem sie platziert wird. Dies ist möglich, da HTL die Syntax von HTML versteht.

Bei Annahme der folgenden logic.js-Datei:

use(function () {
    return {
        link:  "#my link's safe",
        title: "my title's safe",
        text:  "my text's safe"
    };
});

Das anfängliche Beispiel hat die folgende Ausgabe zur Folge:

<p>
    <a href="#my%20link%27s%20safe" title="my title&#39;s safe">
        my text&#39;s safe
    </a>
</p>

Beachten Sie, dass die beiden Attribute unterschiedlich mit Escapezeichen versehen wurden, da HTL weiß, dass die Attribute href und src für den URI-Kontext mit Escapezeichen versehen werden müssen. Falls der URI mit javascript: beginnt, wird das Attribut vollständig entfernt, sofern der Kontext nicht explizit in etwas anderes geändert wurde.

Weitere Einzelheiten zur Steuerung des Escapings finden Sie im Abschnitt Anzeigekontext der Ausdruckssprache der HTL-Spezifikationen.

Automatisches Entfernen leerer Attribute

Siehe folgendes Beispiel:

<p class="${properties.class}">some text</p>

Wenn der Wert der Eigenschaft class leer ist, entfernt die HTML-Vorlagensprache automatisch das gesamte class-Attribut aus der Ausgabe.

Wieder ist dies deswegen möglich, weil HTL die HTML-Syntax versteht und daher Attribute mit dynamischen Werten nur dann anzeigen kann, wenn ihr Wert nicht leer ist. Dies ist sehr praktisch, da es das Hinzufügen eines Bedingungsblocks um die Attribute herum vermeidet, was das Markup ungültig und unlesbar gemacht hätte.

Zusätzlich ist der im Ausdruck platzierte Variablentyp von Bedeutung:

  • Zeichenfolge:

    • nicht leer: Legt die Zeichenfolge als Attributwert fest.
    • leer: Entfernt das Attribut vollständig.
  • Zahl: Legt den Wert als Attributwert fest.

  • Boolesch:

    • true: Zeigt das Attribut ohne Wert an (als ein boolesches HTML-Attribut
    • false: Entfernt das Attribut vollständig.

Hier ist ein Beispiel dafür, wie ein boolescher Ausdruck die Kontrolle über ein boolesches HTML-Attribut ermöglichen würde:

<input type="checkbox" checked="${properties.isChecked}"/>

Zum Festlegen von Attributen ist die Anweisung data-sly-attribute möglicherweise ebenfalls hilfreich.

Allgemeine Muster mit HTL

In diesem Abschnitt werden ein paar allgemeine Szenarien vorgestellt. Zudem wird aufgezeigt, wie diese mit der HTML-Vorlagensprache am besten gelöst werden können.

Laden von Client-Bibliotheken

In HTL werden Client-Bibliotheken über eine durch AEM bereitgestellte Hilfsvorlage geladen, auf die über data-sly-use zugegriffen werden kann. In dieser Datei sind drei Vorlagen verfügbar, die über data-sly-call abgerufen werden können:

  • css – Lädt nur die CSS-Dateien der referenzierten Client-Bibliotheken.
  • js – Lädt nur die JavaScript-Dateien der referenzierten Client-Bibliotheken.
  • all – Lädt alle Dateien der referenzierten Client-Bibliotheken (CSS und JavaScript).

Jede Hilfsvorlage erwartet eine categories-Option für das Referenzieren der gewünschten Client-Bibliotheken. Diese Option kann entweder ein Array von Zeichenfolgenwerten oder eine Zeichenfolge mit einer durch Kommata getrennten Werteliste sein.

Es folgen zwei kurze Beispiele.

Vollständiges Laden mehrerer Client-Bibliotheken gleichzeitig

<sly data-sly-use.clientlib="/libs/granite/sightly/templates/clientlib.html"
     data-sly-call="${clientlib.all @ categories=['myCategory1', 'myCategory2']}"/>

Referenzieren einer Client-Bibliothek in unterschiedlichen Abschnitten einer Seite

<!doctype html>
<html data-sly-use.clientlib="/libs/granite/sightly/templates/clientlib.html">
    <head>
        <!-- HTML meta-data -->
        <sly data-sly-call="${clientlib.css @ categories='myCategory'}"/>
    </head>
    <body>
        <!-- page content -->
        <sly data-sly-call="${clientlib.js @ categories='myCategory'}"/>
    </body>
</html>

Wenn in diesem Beispiel die HTML-Elemente head und body in verschiedenen Dateien platziert sind, müsste die Vorlage clientlib.html in jede Datei geladen werden, die sie benötigt.

Der Abschnitt über die Template- und Call-Anweisungen in der HTL-Spezifikation enthält weitere Details darüber, wie das Deklarieren und Aufrufen solcher Templates funktioniert.

Weitergeben von Daten an den Client

Die beste und eleganteste Art, Daten an den Client zu übermitteln, ist im Allgemeinen die Verwendung von data-Attributen, erst recht aber bei HTL.

Das folgende Beispiel zeigt, wie die Logik (die auch in Java geschrieben werden könnte) verwendet werden kann, um das Objekt, das an den Client übergeben werden soll, bequem in JSON zu serialisieren, das dann einfach in ein data-Attribut eingefügt werden kann:

<!--/* template.html file: */-->
<div data-sly-use.logic="logic.js" data-json="${logic.json}">...</div>
/* logic.js file: */
use(function () {
    var myData = {
        str: "foo",
        arr: [1, 2, 3]
    };

    return {
        json: JSON.stringify(myData)
    };
});

Von dort aus ist es einfach, sich vorzustellen, wie ein Client-seitiges JavaScript-Element auf dieses Attribut zugreifen und das JSON-Objekt erneut analysieren kann. Dies wäre z. B. das entsprechende JavaScript, das in eine Client-Bibliothek einzufügen wäre:

var elements = document.querySelectorAll("[data-json]");
for (var i = 0; i < elements.length; i++) {
    var obj = JSON.parse(elements[i].dataset.json);
    //console.log(obj);
}

Arbeiten mit Client-seitigen Vorlagen

Ein spezieller Fall, in dem die im Abschnitt Einschränkungen für Erhebungen spezieller Kontexte erläuterte Technik legitimerweise verwendet werden kann, besteht im Schreiben von Client-seitigen Vorlagen (beispielsweise Handlebars), die sich in scrip-Elementen befinden. Der Grund, weshalb diese Technik in diesem Fall sicher verwendet werden kann, besteht darin, dass das script-Element nicht wie angenommen JavaScript-Elemente enthält, sondern weitere HTML-Elemente. Im Folgenden finden Sie ein Beispiel, wie dies funktionieren würde:

<!--/* template.html file: */-->
<script id="entry-section" type="text/template"
    data-sly-include="entry-section.html"></script>

<!--/* entry-section.html file: */-->
<div class="entry-section">
    <h2 data-sly-test="${properties.entrySectionTitle}">
        ${properties.entrySectionTitle}
    </h2>
    {{#each entry}}<h3><a href="{{link}}">{{title}}</a></h3>{{/each}}
</div>

Wie oben gezeigt, kann das Markup, das in das script-Element aufgenommen wird, HTL-Blockanweisungen enthalten, und die Ausdrücke müssen keine expliziten Kontexte angeben, da der Inhalt der Handlebars-Vorlage in einer eigenen Datei isoliert wurde. In diesem Beispiel wird zudem gezeigt, wie die Server-seitig ausgeführte HTL (wie im Element h2) mit einer Client-seitig ausgeführten Vorlagensprache, beispielsweise Handlebars (im Element h3 gezeigt), kombiniert werden kann.

Eine modernere Technik bestünde jedoch darin, stattdessen das HTML-Element template zu verwenden. Damit würde sich die Notwendigkeit erübrigen, die Inhalte der Vorlagen in separaten Dateien zu isolieren.

Einschränkungen für Erhebungen spezieller Kontexte

In den besonderen Fällen, in denen es erforderlich ist, die Beschränkungen des Skript-, Stil- und Kommentar-Kontextes zu umgehen, ist es möglich, deren Inhalt in einer separaten HTL-Datei zu isolieren. Alles in seiner eigenen Datei Befindliche wird durch HTL als normales HTML-Fragment interpretiert unter Nichtberücksichtigung des begrenzten Kontexts, in dem es möglicherweise enthalten war.

Ein Beispiel finden Sie weiter unten im Abschnitt Arbeiten mit Client-seitigen Vorlagen.

VORSICHT

Diese Technik kann zu Schwachstellen beim Cross-Site-Scripting (XSS) führen, und die Sicherheitsaspekte sollten sorgfältig geprüft werden, wenn diese Technik verwendet werden muss. Es gibt für gewöhnlich bessere Möglichkeiten für die Implementierung desselben Aspekts, als sich auf diese Praktik zu verlassen.

Auf dieser Seite