Web-1/Templates
Inleiding
Een website bestaat gewoonlijk uit meerdere pagina's. Deze pagina's verschillen wat betreft hun inhoud, maar gebruiken bij voorkeur eenzelfde vorm en opmaak. Ook zijn sommige onderdelen voor alle pagina's gelijk, bijvoorbeelde de header, footer, en navigatie. Voor een consistente opmaak van de pagina's gebruiken we gemeenschappelijke CSS-bestanden. Maar voor de gemeenschappelijke onderdelen van de verschillende pagina's biedt HTML geen oplossing. In het eerste voorbeeld hieronder zien we welke problemen dat met zich meebrengt, doordat de verschillende html-pagina's dezelfde tekst (html-code) dupliceren. In het volgende voorbeeld zien we hoe je die duplicatie kunt voorkomen met behulp van templates ofwel sjablonen. Dergelijke templates vind je bij alle soorten software voor het maken van websites. Ook op veel andere plekken in de ICT zijn templates zinvol te gebruiken. We geven daarvan enkele voorbeelden.
Website met meerdere pagina's
We ontwikkelen eerst een website met meerdere pagina's door de flow voor een enkele pagina te herhalen. De voorbeeld-website bestaat uit twee pagina's. We gebruiken de volgende flow, met de onderstaande inhoud van de beide pagina's:
NodeRed code: /Nodered-templates-0
Home template:
<!doctype html>
<html>
<head>
<title>Welcome</title>
</head>
<body>
<header>
<h3>Mijn eerste NodeRed website</h3>
</header>
<h3>Welkom</h3>
<p>
Welkom! Dit is de Home-pagina.
</p>
<nav>
<a href="/">Home</a>
<a href="/about">Over deze website</a>
</nav>
</body>
</html>
About template:
<!doctype html>
<html>
<head>
<title>About</title>
</head>
<body>
<header>
<h3>Mijn eerste NodeRed website</h3>
</header>
<h3>Over deze website</h3>
<p>
Hier komt een uitleg over deze website.
</p>
<nav>
<a href="/">Home</a>
<a href="/about">Over deze website</a>
</nav>
</body>
</html>
Opdracht
- Maak de bovenstaande flow (op een nieuwe flow-pagina)
- kopieer eventueel de code van /Nodered-templates-0
- en test deze flow met een browser
- Voeg een derde pagina toe
- Bedenk hiervoor zelf een naam (URL) en een inhoud;
- Zorg ervoor dat de opmaak van deze pagina's dezelfde is;
- Zorg ervoor dat deze pagina dezelfde header en navigatie heeft als de andere pagina's;
- Werk de navigatie voor de andere pagina's ook bij, zodat alle drie pagina's naar elkaar verwijzen.
Duplicatie van tekst (html-code)
In de html-tekst voor de verschillende pagina's zie je dezelfde teksten voorkomen, voor <header>
en <nav>
. Deze teksten zijn gelijk omdat we de vorm van de beide pagina's dezelfde willen houden.
In de ICT/Informatica is het een "doodzonde" om eenzelfde tekst op verschillende plekken te herhalen: Don't Repeat Yourself (DRY). Een belangrijke reden hiervoor is dat dergelijke teksten in de toekomst waarschijnlijk zullen veranderen; je moet dan dezelfde tekst op meerdere plekken aanpassen. Dat is al veel (overbodig) werk, met bovendien het risico dat je hierbij fouten maakt, of dat je iets vergeet. Je hebt dan een manier nodig om ervoor te zorgen dat je die tekst (van de header, footer, navigatie-deel) maar op één plek beschrijft.
- Wat zijn andere voorbeelden van (programma)teksten die je op meerdere plekken gebruikt, maar die je op één plek beschrijft (definieert)?
Templates
Een template is een vorm die voor een deel nog ingevuld moet worden. NodeRed gebruikt de template-notatie Mustache, zie https://mustache.github.io/. In Mustache geef je met behulp van {{naam}}
aan dat daar de waarde van de variabele naam
ingevuld moet worden. Meestal is deze variabele een veld (eigenschap) van een object dat samen met het template meegegeven wordt:
template | | ==> Mustache ==> (html) tekst object |
Voorbeeld:
"Dit is een {{x}} voorbeeld van een {{y}} template" | | ==> Mustache ==> "Dit is een simpel voorbeeld van een Mustache template" {"x": "simpel", "y": "Mustache"} |
Suggestie: probeer dit (en andere) voorbeeld(en) op de Mustache demo-pagina (https://mustache.github.io/#demo). Je moet het object daar in JSON-notatie geven. Je vindt daar ook template-parameters van de vorm {{#list}}
. Ga na waarvoor die gebruikt worden.
Website met templates
We maken nu een versie van dezelfde website waarin we de gemeenschappelijke teksten in aparte nodes onderbrengen. Deze teksten vullen we dan in in de templates van de betreffende pagina's.
Flow:
NodeRed-code: /Nodered-templates-1
Met als inhoud van de verschillende nodes:
Page-template:
<!doctype html>
<html>
<head>
<title>My Website</title>
</head>
<body>
<header>
<h3>Mijn eerste NodeRed website</h3>
</header>
{{{pagecontents}}}
<nav>
<a href="/">Home</a>
<a href="/about">Over deze website</a>
</nav>
</body>
</html>
<h3>Welkom</h3>
<p>
Welkom! Dit is de eerste pagina.
</p>
<h3>Over deze website</h3>
<p>
Hier komt een uitleg over deze website.
</p>
Merk op dat we {{{pagecontents}}}
gebruiken in plaats van {{pagecontents}}
: de waarde van de parameter bestaat uit html-tekst, niet uit tekst waar eventule html-symbolen letterlijk genomen moeten worden. (Zie verderop voor meer uitleg.)
- ga na wat er gebeurt als je
{{...}}
gebruikt.
Opzet van website met templates
We kunnen in een template meerdere parameters opnemen. Vaak zijn enkele van deze parameters berekend uit de verwerking van de URL. Voor veel van de voorbeelden in de volgende hoofdstukken gebruiken we daarom de volgende structuur van een flow:
- http input-node
- functie node:
- voor het verwerken van de input-data;
- eventueel zetten van enkele template parameters
- template-node met JavaScript-code voor deze URL ("clientscript")
- template-node met html-inhoud voor deze URL ("pagecontents");
- template-node met gemeenschappelijke html-code.
In het onderstaande voorbeeld gebruiken we de functie-nodes alleen voor het zetten van enkele template-parameters.
NodeRed-code: /Nodered-templates-2
Page-template:
<!doctype html>
<html>
<head>
<title>{{pagetitle}}</title>
</head>
<body>
<header>
<h3>Mijn eerste NodeRed website</h3>
</header>
{{{pagecontents}}}
<nav>
<a href="/">Home</a>
<a href="/about">Over deze website</a>
</nav>
</body>
</html>
Process-home functie:
msg.pagetitle = "Home";
return msg;
Opdracht
- Pas de flow met 3 pagina's die je eerder gemaakt hebt, aan naar een flow met templates en een gemeenschappelijke "staart".
- laat op alle pagina's het IP-adres van de aanvrager zien.
- dit kun je vinden via: msg.req.headers["x-forwarded-for"]
Meer over templates
Gebruik van templates voor websites
Voor websites gebruiken we templates op twee manieren:
- om gemeenschappelijke stukken tekst van verschillende webpagina's, zoals de header en de footer, in aparte bestanden (of nodes) onder te brengen;
- voor dynamische webpagina's, waarbij we dynamisch bepaalde gegevens invullen in de webpagina.
- zoals: naam van de gebruiker, aantal artikelen in de winkelwagen, inhoud van de winkelwagen, enz.
- dynamisch betekent hier: niet bekend op het moment van het ontwerpen van de website.
Waarden met html-symbolen
Bij HTML-templates moet je er rekening mee houden dat de waarde van een variabele die je invult in een template, zelf ook weer HTML-symbolen kan bevatten. In zo'n geval zijn er twee mogelijkheden:
- de tekst moet letterlijk gezien worden: de HTML-symbolen worden voorzien van een "escape" waardoor deze letterlijk in het uiteindelijke resultaat terecht komen;
- de tekst moet als HTML-tekst gezien worden: eventuele HTML-symbolen worden in het HTML-resultaat opgenomen, en zijn in het uiteindelijke resultaat niet meer zichtbaar.
In Mustache maak je dit onderscheid, door {{...}}
te gebruiken voor geval (1), en {{{...}}}
voor geval (2).
Voorbeeld:
template: Dit is een {{{soort}}} voorbeeld. soort: <em>simpel</em>
geeft (als html-code):
Dit is een <em>simpel</em> voorbeeld.
Dit wordt in de browser weergegeven als: Dit is een simpel voorbeeld.
Als je {{...}}
gebruikt is het html-resultaat:
Dit is een <em>simpel</em> voorbeeld.
met als uiteindelijk resultaat in de browser: Dit is een <em>simpel</em> voorbeeld.
condities in templates
Als een waarde niet gedefinieerd is, of "leeg", heeft het niet altijd zin om deze in te vullen. In zo'n geval wil je dan een andere tekst kunnen genereren. Het volgende voorbeeld heeft als resultaat "Welkom Hans" als user="Hans", en "Login", als user="":
{{#user}}
Welkom {{user}}
{{/user}}
{{^user}}
Login
{{/user}}
herhaling in templates
Als een waarde uit een reeks objecten bestaat, dan kunnen we voor elk object een string genereren. Voorbeeld:
{{#users}}
<li> {{name}} </li>
{{/users}}
met users=[{name: "Hans"}, {name: "Anna"}, {name: "Joan"}]
, heeft als resultaat
<li> Hans </li>
<li> Anna </li>
<li> Joan </li>
Je kunt deze herhaling combineren met een conditie, bijvoorbeeld om een lege namenlijst apart af te handelen.
herhaling in templates
escape-mogelijkheden
Mustache geeft de haakjes {{...}}
een speciale betekenis. Dit betekent dat je dergelijke haakjes niet zomaar in je normale tekst kunt gebruiken, met hun "gewone" betekenis. Je kunt deze haakjes op de volgende manier wel in je template-tekst opnemen:
- gebruik html-escape-codes voor deze symbolen: { en }. { en }
Escapes in andere talen
Het gebruik van dergelijke escapes kom je in veel talen tegen: een taal geeft sommige symbolen een speciale betekenis. Als je die symbolen zelf wilt weergeven, zonder hun speciale betekenis, dan heb je een escape-mechanisme nodig.
Opdrachten
- wat is het escape-mechanisme in html? Hoe kun je in html de volgende tekst weergeven: "In html gebruik je <p>...</p> om een paragraaf aan te geven"?
- wat is het escape-mechanisme in JavaScript strings? Hoe kun je de volgende string weergeven: "In JavaScript gebruik je ' en " als string-delimiters."?
client-side templates
- templates in client; client-side scrpting (e.g., Vue; html?)
- (er is ook een html template element; maar dat is nog vrij lastig te gebruiken, vanuit JS)
Templates in andere talen
Templates kom je in veel verschillende (programmeer)talen en omgevingen tegen. Bij het maken van website zijn deze essentieel, omdat HTML (nog) geen goed gereedschap heeft om gemeenschappelijke html-teksten in aparte bestanden onder te brengen. (In andere talen heb je daarvoor procedures/functies of modules.)
Template-engines
Er zijn allerlei soorten "template engines".
Mustache
Mustache (https://mustache.github.io/) is een eenvoudige template-taal die niet gebonden is aan een bepaalde programmeertaal: je kunt hiermee allerlei soorten teksten parametriseren. Mustache wordt in veel verschillende omgevingen gebruikt, onder andere in NodeRed.
Voor het gebruik in webservers kom je onder andere de volgende template-systemen tegen:
Voorbeelden:
- Python, met Flask (http://flask.pocoo.org/) als webserver-software: Jinja2 (http://jinja.pocoo.org/docs/2.9/)
- JavaScript, met Node ( ) als webserver-software: Mustache ( )
- (React - JSX?)
- (ELM - ??)
- Jekyll: Liquid (https://shopify.github.io/liquid/)
- Jekyll is voor statische sites, maar ook in dat geval wil je pagina's hebben met eenzelfde structuur.
- Jekyll wordt gebruikt voor GitHub Pages.
- Ruby: je kunt uit veel template-engines kiezen (https://www.ruby-toolbox.com/categories/template_engines.html)
Opmerking:
- voor een deel gebruiken we deze templates om een HTML-probleem op te lossen: in HTML heb je geen mogelijkheden voor abstractie en parametrisatie. Je moet dan wel een extern middel gebruiken;
- sommige template-engines zijn "universeel" voor tekstbestanden, en niet gekoppeld aan de taal waarin deze bestanden geschreven zijn.
NB: hoewel PHP eigenlijk zelf een template-taal is, wordt dit ook vaak in combinatie met andere template-engines gebruikt. O.a.: smarty (zie bijv. Mediawiki - Widgets).
Een andere voorbeeld van een mix van HTML en programmeertaal: JSX, als onderdeel van React.
Andere voorbeelden van het gebruik van templates
Andere voorbeelden van het gebruik van templates:
- mail/merge: je combineert een document-template (bijv. in Word) met een lijst van geadresseerden (bijv. in Excel), zodat je een gepersonaliseerde brief krijgt voor elke geadresseerde.
- Python string templates, handig voor bijvoorbeeld complexe print-opdrachten
Een voorbeeld van Python string templates:
>>> tpl = "de som van {a} en {b} is {c}"
>>> tpl.format(a=12, b=13, c=12+13)
'de som van 12 en 13 is 25'