Templates
Templatesysteme und Hamlet
Templatesysteme, auch Template-Engines genannt, verarbeiten Zeichenketten unterschiedlicher Formate, indem sie darin enthaltene Platzhalter mit für den Verarbeitungsprozess übergebenen Werten ersetzen. Auf diese Weise können Programmcode und Markup konzeptuell und praktisch voneinander getrennt werden.[1]
Yesod verwendet zwecks Nutzung von Templates das Paket Hamlet. Dieses Paket umfasst die Templatesprachen Hamlet für HTML, Cassius für CSS und Julius für JavaScript. Für CSS-Auszeichnungen steht zusätzlich eine weitere Templatesprache mit dem Namen Lucius zur Verfügung. In diesem Kapitel werden sowohl die Verwendung dieser Templatesprachen, als auch deren Syntax und Möglichkeiten anhand von praktischen Beispielen erklärt.
Konzept von Hamlet zur Generierung von HTML
Bei Hamlet handelt es sich um das standardmäßig von Yesod verwendete Paket zur Anwendung von
Templates. Der Ansatz der darin beschriebenen Templatesprache mit dem gleichen Namen ist, sämtliche
Redundanzen aus HTML-Auszeichnungen zu entfernen, gleichzeitig
jedoch keine komplett neue Syntax einzuführen, welche erst zu erlernen ist. Grob zusammengefasst lässt sich
sagen, dass Hamlet eine zu HTML ähnliche Templatesprache
ist, bei der die Struktur des Dokumentbaums durch den Grad der Einrückung bestimmt wird. Ein hervorragender
Nebeneffekt dieser Konventionen ist die Tatsache, dass ein erheblicher Anteil von ungültigem
HTML auf diese Weise von Hamlet nicht generierbar ist.
Beispielsweise wäre die Erzeugung des nicht wohlgeformten
HTML-Code-Fragments
<p><strong>Text</p></strong>
demnach mit Hamlet nicht möglich.
Die Gültigkeit von HTML-Bestandteilen wird in diesem
Zusammenhang jedoch nicht geprüft. Für einen solchen Mechanismus wäre es notwendig, den gesamten
Kontext eines HTML-Dokumentbaums zu erfassen und nicht
nur das Fragment.
Es folgt ein Beispiel für Templatecode, welcher von Hamlet interpretiert werden kann.
1<div #an-id style=width:500px;>
2 <a href=@{MyDest}>I'm a link!
3 <ul .a-class>
4 $forall p <- people
5 <li>#{familyName p}, #{firstName p}
Attributwerte müssen innerhalb von Hamlet nicht in Anführungszeichen gesetzt werden. Im generierten HTML-Markup geschieht dies zugunsten wohlgeformter HTML-Dokumente jedoch immer. Die Angabe von ID-Kennungen oder Klassenzugehörigkeiten kann angelehnt an die CSS-Entsprechungen abgekürzt erfolgen. Das Dollarzeichen leitet eine Kontrollstruktur ein.[2]
Interpolationen
Fundamentaler Mechanismus von Hamlet sind die sogenannten Interpolationen. Eine solche wird durch das Interpolationssymbol, gefolgt von einem Parameter, welcher in geschweiften Klammern eingeschlossen ist, gekennzeichnet. Interpolationen sind mit Umwandlungen gleichzusetzen, bei denen Platzhalter typsicher durch entsprechende Inhalte ersetzt werden. Je nach Anwendungszweck stehen drei unterschiedliche Interpolationsarten zur Verfügung. Um Variablen einzubinden, dient die Variableninterpolation, welche mit dem Rautensymbol # eingeleitet wird. Zwei Beispiele für eine solche Umwandlung finden sich im Codeausschnitt in der fünften Zeile. Alle auf diese Art in die Templates eingebundenen Daten werden im Zuge der Verarbeitung automatisch escaped, was Cross-Site-Scripting-Attacken entgegen wirkt.
In Zeile 2 erfolgt zudem eine so genannte URL-Interpolation. Hierbei erfolgt ein Verweis auf eine Adresse, welche beispielsweise im Rahmen des Routingsystems definiert wurde und anhand eines Haskell-Werts und einer Renderfunktion in einen String konvertiert werden kann. Auf diese Weise muss bei einer Änderung der Adresse nicht jeder einzelne Verweis angepasst werden. URL-Umwandlungen werden durch das At-Zeichen @ gekennzeichnet.
Neben diesen beiden Umwandlungsarten existiert zudem noch die Möglichkeit der Einbettung von weiteren Templates, so dass häufig verwendete HTML-Bestandteile entsprechend in eigene Templatedateien ausgelagert werden können. Die Einbettung von Subtemplates wird in Hamlet mithilfe des Einfügezeichens ^ eingeleitet.
Cassius unterstützt sowohl Variablen-, als auch die URL-Interpolation. Julius interpretiert neben diesen beiden Interpolationsarten zudem auch die Einbettung weiterer Templates.[3]
Syntax von Cassius und Julius
Das folgende Beispiel zeigt eine Stylesheet-Definition über die Templatesprache Cassius.
1a
2 color: darkgreen
3h1
4 border-bottom: 1px solid grey
Wie zu sehen ist, erfolgt auch bei Cassius die Definition der Struktur über den Grad der Einrückung. Julius dient im Gegensatz zu Hamlet und Cassius dazu, den unverarbeiteten JavaScript-Code schlicht an Yesod durchzureichen. Somit sind bei Julius beispielsweise auch die Whitespaces nicht signifikant.[4]
Lucius
Mit Lucius existiert zusätzlich noch eine weitere Templatesprache, welche es ermöglicht, CSS-Beschreibungen ineinander zu schachteln. So würde die Lucius-Beschreibung
1foo, bar {
2 baz, bin {
3 color: red;
4 }
5}
in die CSS-Repräsentation
1foo baz, foo bin, bar baz, bar bin {
2 color: red;
3}
umgewandelt werden.[5]
Anwendung von Templates
Bereits im vorigen Kapitel kam Hamlet bei der Betrachtung des einleitenden Beispiels zur Anwendung. An einem weiteren Beispiel soll die Funktionsweise der Templatesysteme verdeutlicht werden.
1 let name = "Michael" :: String
2 let nameId = "name" :: String
3
4 addHamlet [hamlet|
5<h1>Hello World!
6<p>
7 Welcome to my system. Your name is #
8 <span ##{nameId}>#{reverse $ take 2 $ reverse name}
9 . Enjoy your stay!
10<p
11 <a href=@{BlogR "einstein" "relativity"}>general relativity
12|]
Zu Beginn werden zwei Variablen definiert, welche aus dem Template heraus genutzt werden sollen.
In Zeile 4 kommt dann eine so genannte Quasi-Quotation zur Anwendung. Dabei wird die Zeichenkette auf
der rechten Seite des Trennstriches von dem auf der linken Seite des Trennstriches angegebenen Quoter
interpretiert und im Zuge der Kompilierung in Haskell-Code umgesetzt. Die Funktion addHamlet
sorgt dann
für die entsprechende Speicherung des HTML-Fragments zur späteren
Ausgabe an den Client. Der Mechanismus entstammt dem Konzept der Metaprogrammierung mit «Template Haskell», zu welchem ein
eigenständiger Seminarbeitrag
entstanden ist. Anhand der Quasi-Quotations lassen sich die beschriebenen Templates einbinden. In
Zeile 8 werden zusätzlich zwei Variablen interpoliert. Dabei steht die erste Variable für die ID-Kennung des
<span>
-Tags, wodurch sich das doppelte Rautensymbol erklärt. Bei der zweiten Variable werden
mithilfe von Funktionen aus Haskell lediglich die letzten beiden Zeichen ausgegeben. Eine
URL-Interpolation findet sich in Zeile 11. In diesem Fall
werden zudem zwei Argumente an die Route übergeben. Die entsprechende Definition der Route könnte beispielsweise
wie folgt aussehen.
1mkYesod "RouteExample" [parseRoutes|
2/ HomeR GET
3/blog/#Author/#Title BlogR GET
4|]
In der Praxis wird man die Templates bevorzugt auslagern und entsprechend von einer statischen Quelle
laden. Dies kann mithilfe der Funktion hamletFile
bzw.
cassiusFile
oder juliusFile
unter der Angabe des Dateipfades erfolgen. Zumeist
wird der Einbindung eines Templates auch eine Funktion vorangestellt, welche den generierten
HTML-Code in ein bestehendes Dokumentgerüst einbettet.
In einem für Yesod neu generierten Projekt ist zu diesem Zweck standardmäßig eine Funktion
defaultLayout
vordefiniert, welche entsprechend der eigenen Vorstellungen angepasst werden
kann.[6][7]
- [1] Thomas Walter, Kompendium der Web-Programmierung: Dynamische Web-Sites, S. 435.
- [2] Michael Snoyman, Yesod Web Framework Book: Templates, Introduction http://www.yesodweb.com/show/topic/102
- [3] Michael Snoyman, Yesod Web Framework Book: Templates, Type Safety http://www.yesodweb.com/show/topic/109
- [4] Michael Snoyman, Yesod Web Framework Book: Templates, Basic syntax http://www.yesodweb.com/show/topic/110
- [5] Michael Snoyman, Yesod Web Framework Book: Templates, Lucius http://www.yesodweb.com/show/topic/108
- [6] Michael Snoyman, Yesod Web Framework Book: Templates, Variables http://www.yesodweb.com/show/topic/112
- [7] Michael Snoyman, Yesod Web Framework Book: Templates, Function Application http://www.yesodweb.com/show/topic/107