Heist


... [ Programmiersprachen und Sprachsysteme ] ... [ Das Snap Framework ] ... [ << Snaplets ] ... [ Abschluss >> ] ...

Kommen wir nun zum letzten Kapitel, der Templating Engine von Snap. Es basiert auf dem Lift Web Framework das in Scala geschrieben ist. Die Templates bestehen aus HTML bzw. XML Fragmenten. Diese Fragmente können mehrere root-Elemente besitzen. Heist ist unabhängig von Snap und kann auch von anderen Haskell-Web-Frameworks genutzt werden.


Ziel

Das Ziel von Heist ist klar. Der Template Code soll abstrahiert werden, um Wiederholungen zu vermeiden. D.h. wir wollen die Navigation möglichst nur einmal schreiben und nicht für jede Seite einzeln. Außerdem soll Code und View getrennt werden. So können wir einen Frontendler die Templates erstellen lassen und danach die Logik in die Templates hinzufügen. Zudem müssen wir nicht mit HTML in Haskell hantieren. Um das zu realisieren bietet Snap uns Tags und Splices.


Tags

Bind

Es gibt drei verschiedene Möglichkeiten in Heist, mit denen man innerhalb von Heist-Templates Inhalte einbinden kann.

1
<bind tag="tagname" />

Die Erste ist bind. Mit diesem kann man HTML-Elemente erstellen, die man an anderer Stelle einbinden kann. Nehmen wir das folgende Beispiel:

1
2
3
<bind tag="longName">
	Captain Fantastic Faster Than Superman Spiderman Batman Wolverine Hulk And The Flash Combined
</bind>

Nun können wir im nachfolgenden HTML Code und auch in anderen Templates, die diese Stück HTML einbinden, das Tag < longName /> benutzen. So wird aus:

1
2
3
4
5
<div>
	Der längste Name der Welt ist <longName/>. 
	Er gehört dem Studenten George Garrett, der seinen Namen zu langweilig fand und ihn deshalb in <longName/> änderte. 
	<longName/> sagt: "Ich wollte einzigartig sein."
</div>

Daraus erstellt Heist dann:

1
2
3
4
5
<div>
	Der längste Name der Welt ist Captain Fantastic Faster Than Superman Spiderman Batman Wolverine Hulk And The Flash Combined. 
	Er gehört dem Studenten George Garrett, der seinen Namen zu langweilig fand und ihn deshalb in Captain Fantastic Faster Than Superman Spiderman Batman Wolverine Hulk And The Flash Combined änderte. 
	Captain Fantastic Faster Than Superman Spiderman Batman Wolverine Hulk And The Flash Combined sagt: "Ich wollte einzigartig sein."
</div>

So spart man sich viel Tipperei, vermeidet Wiederholungs-Fehler und schafft eine höhere Abstraktionsebene.
Zudem lassen sich auch Attribute auf diese Art substituieren. Dafür muss allerdings eine leicht abgewandelte Syntax genutzt werden:

1
2
3
4
5
6
<!-- Das bind Tag -->
<bind tag="wedel">http://www.fh-wedel.de</bind>
<!-- Im Template kann man dann folgendes angeben -->
<a href="${wedel}">FH Wedel</a>
<!-- Heist substituiert dann das Attribut wie folgt -->
<a href="http://www.fh-wedel.de">FH Wedel</a>

Apply

Die zweite Möglichkeit ist apply. Mit apply können andere Templates in das aktuelle Template eingebunden werden. Nehmen wir mal das klassische Beispiel einer Navigation und wir haben folgende Datei nav.tpl:

1
2
3
4
5
<ul>
	<li><a href="/">Home</a></li>
	<li><a href="/faq">FAQ</a></li>
	<li><a href="/kontakt">Kontakt</a></li>
</ul>

Wir können nun in anderen Templates mit bind das nav-Template einbinden. Dadurch müssen wir die Navigation immer nur an einer einzigen Stelle ändern und bekommen eine konsistente Navigation auf allen Seiten. Nachfolgend der Inhalt der index.tpl:

1
2
3
4
5
6
7
8
9
<html>
	<head>
		<title>Home Page</titel>
	</head>
	<body>
		<h4>Überschrift</h4>
		<apply template="nav"/>
	</body>
</html>

Die apply-Node wird durch den Inhalt des Templates nav.tpl substituiert. Wichtig ist hierbei, dass der Template-Name ohne die Dateiendung .tpl angegeben wird und dieses Template auch im selben Verzeichnis oder oberhalb der index.tpl, bzw. des anfragenden Templates, in der Verzeichnisstruktur liegt. Dabei sucht Heist bis zum Root des Template-Verzeichnisses. Diese liegt im Normalfall im Projektordner unter ./snaplets/heist/. Wird das Template nicht gefunden, so gibt es einen Fehler während des Serverstarts.

Apply-Content

Man kann das apply-Tag auch mit Inhalt versehen und dadurch ein Grundgerüst erstellen und einzelne Seiten in dieses Grundgerüst laden. Zur Veranschaulichung wieder ein kleines Beispiel. Wir haben unser Grundgerüst in der default.tpl:

1
2
3
4
5
6
7
8
9
<html>
	<head>
		<title>Home Page</titel>
	</head>
	<body>
		<h4>Überschrift</h4>
		<apply-content/>
	</body>
</html>

Dieses Beispiel sieht ähnlich aus, wie das vorige. Beim Vorigen wurde allerdings ein anderes Template in das Grundgerüst geladen. Bei dem jetzigen Beispiel können wir in anderen Templates das Grundgerüst angeben und der Inhalt wird dann an die Stelle von < apply-content /> substituiert. Ein Beispiel für ein aufzurufendes Template:

1
2
3
4
<apply template="default">
	<h1>Home Page</h1>
	<p>Willkommen auf unserer Homepage</p>
</apply>

Kombination von bind und apply

bind und apply lassen sich auch kombinieren. So können mehrere Teilstücke in ein Grundgerüst eingebunden werden. Z.B. haben wir auf jeder Seite einen Kopfteil, eine Navigation und einen Inhalt. Dazu einmal unsere abgeänderte default.tpl:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<html>
	<head>
		<title>Home Page</title>
	</head>
	<body>
		<header />
		<navigation />
		<content />
	</body>
</html>

Nun können wir unsere home.tpl wie folgt definieren:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<apply template="default">
	<bind tag="navigation">
		<apply template="nav" />
	</bind>
	<bind tag="header">
		<h1>Home Page</h1>
	</bind>
	<bind tag="content>
		<p>Willkommen auf unserer Home Page</p>
	</bind>
</apply>

Nun wird beim Aufruf der home.tpl die default.tpl geladen. Dabei wird an die Stelle von navigation unsere zuvor definierte Navigation substituiert. Und an die Stellen von header und content der entsprechende Inhalt aus den bind-Tags. Das Resultat sieht dann wie folgt aus:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<html>
	<head>
		<title>Home Page</title>
	</head>
	<body>
		<!-- <header /> -->
		<h1>Home Page</h1>
		<!-- <navigation /> -->
		<ul>
			<li><a href="/">Home</a></li>
			<li><a href="/faq">FAQ</a></li>
			<li><a href="/kontakt">Kontakt</a></li>
		</ul>
		<!-- <content /> -->
		<p>Willkommen auf unserer Home Page</p>
	</body>
</html>

Wir kennen nun Tags, mit denen wir Templates abstrahieren und kleine, leicht wartbare HTML Stücke erzeugen können. Es fehlt aber noch eine Möglichkeit, aus dem HTML auf Haskell Code zugreifen zu können. Genau dafür kommen Splices ins Spiel.


Splices

Splices binden Tags an Haskell Code. Dabei wird der Inhalt der Tags als Parameter an die in Haskell gebundenen Funktionen übergeben. Die Splices müssen Listen von Nodes zurückliefern, die dann beim Response an die Stelle des Tags substituiert werden. Ein einfaches Beispiel ist die Berechnung der Fakultät. Hierzu definieren wir unsere Splice-Funktion wie folgt:

1
2
3
4
5
6
factSplice :: Splice Snap
factSplice = do
	input <- getParamNode
	let text	= T.unpack $ X.nodeText input
			n			= read text :: Int
	return [X.TextNode $ T.pack $ show $ product [1..n]]

Die Funktion liest den Inhalt der übergebenen Node, berechnet mit der Zahl die Fakultät und gibt sie als Liste von Nodes eingepackt zurück. Der Splice muss in der eigenen Heist-Konfiguration eingetragen werden und kann danach in den Templates benutzt werden. Wenn der Splice nun mit dem Wort fact gebunden wird, können wir ein Template wie folgt schreiben:

1
2
3
4
5
6
7
8
9
<html>
	<head>
		<title>Fakultät</title>
	</head>
	<body>
		<h1>Berechnung der Fakultät</h1>
		Die Fakultät von 5 ist <fact>5</fact>.
	</body>
</html>


Hooks

Kommen wir nun zu den Hooks. Hooks sind Funktionen, mit denen man Funktionen registrieren kann, die zu bestimmen Zeitpunkten für ein Template aufgerufen werden.

1
2
3
addOnLoadHook :: (Monad m) => (Template -> IO Template) -> TemplateState m -> TemplateState m
addPreRunHook :: (Monad m) => (Template -> m Template) -> TemplateState m -> TemplateState m
addPostRunHook :: (Monad m) => (Template -> m Template) -> TemplateState m -> TemplateState m

Die Funktionen nehmen jeweils eine Funktion und einen TemplateState und geben einen neuen TemplateState, der die neue Funktion enthält, zurück. Dabei werden die Funktionen von addOnLoadHook für jedes Template aufgerufen, wenn es von der Festplatte geladen wird. Die anderen beiden werden aufgerufen, bevor bzw. nachdem ein Template gerendert wurde. Die drei Hook-Funktionen ergänzen jeweils nur existierende Hooks und überschreiben keine bereits existierenden Hooks.


... [ Programmiersprachen und Sprachsysteme ] ... [ Das Snap Framework ] ... [ << Snaplets ] ... [ Abschluss >> ] ...
generated by schmidt-doku-generator (GitHub)