

Server Side Rendering met EJS
Server Side Rendering met EJS
HERHALING


nodemon
Installeer nodemon als lokale development dependency
npm install nodemon --save-dev
Eventuele configuratie kan in nodemon.json
{
"watch": ["./src"],
"ext": "js, css, html, json"
}
Of als global dependency (= voor jouw computer)
npm install -g nodemon

nodemon
En gebruik dan steeds nodemon (ipv node) om je
Node-applicatie te starten
// package.json
{
// ...
"scripts": {
// ...
"start": "nodemon index.js"
}
}

nodemon
Je kunt de configuratiebestand nodemon.json
optioneel meegeven in het commando, maar dit is alleen nodig als de configuratie een andere bestandsnaam of locatie heeft.
// package.json
{
// ...
"scripts": {
// ...
"start": "nodemon --config nodemon.json ./src/app.js"
}
}

Omgevingsvariabelen
Node v22.x biedt CLI support voor omgevingsvariabelen. De optie --env-file laadt omgevingsvariabelen uit een bestand (relatief aan de huidige directory) en maakt ze beschikbaar aan applicaties via process.env.
node --env-file=.env ./src/app.js
Om na te gaan welke versie van Node bij jou actief is:
node -v

Omgevingsvariabelen
// package.json
{
// ...
"scripts": {
// ...
"start": "nodemon --env-file=.env ./src/app.js"
}
}
Het start script in package.json zou er dus zou kunnen uitzien

Omgevingsvariabelen
Omgevingsvariabelen definieer je vervolgens in een
.env file
// .env
PORT=3000
De library dotenv heb je niet meer nodig sinds node v20.6.+

Omgevingsvariabelen
Plaats .env altijd in
.gitignore
# General
.cache
package-lock.json
# OS specific
.DS_Store
# IDE
.vscode
.idea
# Node
node_modules
.env

.env-example

-
.env-example
toont welke omgevingsvariabelen nodig zijn. - Het bevat geen gevoelige informatie.
- Helpt ontwikkelaars om snel hun eigen
.env
bestand in te stellen. - Zorgt voor een consistente configuratie in verschillende omgevingen.
- Deze zit niet in .gitignore en komt dus wel in de git-repo.
Server Side Rendering met EJS
INTRODUCTIE


EJS (Embedded JavaScript Templates) is a lightweight templating engine for Node.js that enables you to embed JavaScript directly into HTML.
It uses simple <% %>
syntax to inject variables, loops, and conditionals, making it easy to generate dynamic web pages efficiently.

Installatie
Installeer EJS
npm install ejs

Voorbereiding
- Maak een bestand home.ejs aan in je src/views folder
- Typ in home.ejs de tekst Welcome to EJS!
<!DOCTYPE html>
<html lang="nl">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/assets/fontawesome/css/all.min.css">
<link rel="stylesheet" href="/css/main.css">
<title>Mijn eerste EJS applicatie</title>
</head>
<body>
<h1>Welcome to EJS!</h1>
</body>
</html>

EJS- Express
- Koppel nu EJS aan je gemaakte Express applicatie.
import express from "express";
// imports...
// ...
app.set('view engine', 'ejs');
app.set("views", "<pad-naar-views-folder>"); //

EJS - Express
- Maak een nieuwe controller:
PageController.js in de src/controllers folder - Plaats hier een heel eenvoudige functie die een antwoord stuurt naar de client met de render() functie.
export const home = (req, res) => {
res.render('home');
};

EJS - Express
- Importeer de home functie en registreer de controller in Express op de GET - "/" route
app.get('/', home);

EJS - Express
- Wanneer je de server herstart en via je browser de root-route opvraagt, krijg je je tekst te zien.
Welcome to EJS!
// EJS is up & running!
Server Side Rendering met EJS
EXPRESSIONS / TAGS


Basis tags
- EJS tags laten je toe om aan te duiden dat de code die volgt EJS is en dus geen HTML. Om de waarde van een variabele weer te geven, gebruik je <%=
<h2><%= user.name %></h2>

Basis tags
- Met <% kan je bepaalde control flow (loops, conditionals ...) elementen nabootsen, net zoals je dat in Javascript zou doen.
<% if (user) { %>
<h2><%= user.name %></h2>
<% } %>
- EJS tags afsluiten doe je steeds met %>

Geneste Objecten
{
person: {
firstname: "Yehuda",
lastname: "Katz",
},
}
<%= person.firstname %> <%= person.lastname %>

Control flow
- Met forEach kunnen we itereren over een lijst
<ul>
<% persons.forEach(function(person) { %>
<li><%= person.firstname %> <%= person.lastname %></li>
<% }); %>
</ul>
<ul>
<% for (let i=0; i < persons.length; i++) { %>
<li><%= persons[i].firstname %></li>
<% } %>
</ul>
- Of je kan ook een gewone for loop schrijven

HTML-escaping
- Wanneer we <%- gebruiken als opening tag dan zal de tekst niet omgevormd worden door tekens die "escaped" zijn.
- Je kan in deze tool eens bekijken hoe je speciale tekens "escaped" worden.

Handige tags: Cheat Sheet
-
<%= %>
: Outputs the value into the template (HTML escaped). -
<%- %>
: Outputs the unescaped value into the template. -
<% %>
: Executes JavaScript code without outputting anything. -
<% for(var i=0; i < items.length; i++) { %>
: Iterates over an array. -
<% items.forEach(function(item){ %>
: Iterates over an array -
<% if (user) { %>
: Conditional statement. -
<% } %>
: Closes an opened JavaScript block. -
<% include('filename') %>
: Includes another EJS template. -
<% include('filename', { quote: "Get it done!" }) %>
:
Includes another EJS template and passes additional data to it.

Onhandige structuren
Bij het genereren van HTML in EJS kom je vaak situaties tegen waarbij:
-
Code onnodig genest wordt door
<% %>
-blokken. - Vb basislogica moet implementeren
- <% if(...) { %> moet je openen
- dan HTML moet printen
- <% } %> dan de accolade weer moet sluiten
-
Dit kan soms de leesbaarheid beïnvloeden,
zoals bij loops met condities.

📌 __append("...")
-
Wat doet
__append("...")
?__append("...")
voegt HTML of tekst rechtstreeks toe aan de uitvoer binnen een EJS-codeblock- Dit werkt binnen
<% %>
-blokken, waar normaal geen directe uitvoer mogelijk is. - Handig als je content wilt genereren in een
conditie of loop zonder<%= %>
te gebruiken.

📌 __append("...")
<ul>
<% animals.forEach(animal => { %>
<li><%= animal.name %>
<% if (animal.isEndangered) { %>
<span style="color: red;"> 🔴 Bedreigd</span>
<% } %>
</li>
<% }); %>
</ul>
<ul>
<li>Tijger <span style="color: red;"> 🔴 Bedreigd</span></li>
<li>Hond</li>
<li>Panda <span style="color: red;"> 🔴 Bedreigd</span></li>
</ul>
Voorbeeld in EJS zonder __append()
Output als HTML

📌 __append("...")
<ul>
<% animals.forEach(animal => { %>
<li><%= animal.name %>
<% if (animal.isEndangered) {
__append('<span style="color: red;"> 🔴 Bedreigd</span>');
}
%>
</li>
<% }); %>
</ul>
<ul>
<li>Tijger <span style="color: red;"> 🔴 Bedreigd</span></li>
<li>Hond</li>
<li>Panda <span style="color: red;"> 🔴 Bedreigd</span></li>
</ul>
Voorbeeld in EJS met __append()
Zelfde output als HTML

📌 __append("...")
<ul>
<% animals.forEach(animal => {
let output = `<li>${animal.name}`;
if (animal.isEndangered) output += ' <span style="color: red;">🔴 Bedreigd</span>';
output += '</li>';
__append(output);
}); %>
</ul>
Compacter met __append()
<ul>
<% animals.forEach(animal => __append(`<li>${animal.name}${animal.isEndangered ? ' <span style="color: red;">🔴 Bedreigd</span>' : ''}</li>`)); %>
</ul>
Of NOG compacter!

📌 __append("...")
Wanneer is __append("...")
handig?
-
Basislogica binnen een code bloack zoals
- iteraties, condities, variabelen aanpassen.
- HTML 'renderen' binnen een code block (zoals <%- ... ->)
- Minder aparte code blocks
Gebruik het slim, maar met mate!
Te veel __append()
kan je code minder leesbaar maken.
-
⚠ Niet geschikt voor grotere HTML-structuren (minder leesbaar).

Welke data? Context Dumpen
Om alle context (de variabelen) in een EJS-template te dumpen, kun je de JSON.stringify()
functie gebruiken om de volledige context als een JSON-object weer te geven.
Dit is handig voor debugging of om te zien welke data beschikbaar is in je template.
<pre><%= JSON.stringify(locals, null, 2) %></pre>

Lokale context dumpen
<pre><%= JSON.stringify(locals, null, 2) %></pre>
-
locals
bevat alle variabelen die aan de template zijn doorgegeven. -
JSON.stringify(locals, null, 2)
zet de context om in een mooi opgemaakte JSON-string. - De
<pre>
tag zorgt ervoor dat de JSON netjes wordt weergegeven, met behoud van de tabs.
Dit toont alle context (de data) die je naar je EJS-template hebt gestuurd, in een leesbaar formaat binnen de browser.
⚠️ Let op: Zorg ervoor dat je dit alleen gebruikt in een ontwikkelomgeving, aangezien het potentieel gevoelige gegevens kan blootstellen!
Server Side Rendering met EJS
PARTIALS & LAYOUTS


EJS allows for template reuse through includes
(often referred to as "partials").
Partials are normal templates that may be called directly by other templates.

includes
- Je kan een partial registreren door een nieuw .ejs bestand aan te maken in je /views/partials folder.
- Vervolgens kan je je partial in je template gebruiken met:
<%- include('partials/yourFileName.ejs') %>

includes
- EJS does not specifically support blocks, but layouts can be implemented by including headers and footers.
<%- include('partials/header'); -%>
<h1>
Title
</h1>
<p>
My page
</p>
<%- include('partials/footer'); -%>

Layouts in web development refer to reusable template structures that define the overall design and organization of a webpage.
They provide a consistent framework by including common elements like headers, footers, and navigation, allowing content to be dynamically injected while maintaining a uniform look across multiple pages.

Layouts
Terminologie:
- Partials: een deel van je website die op verschillende pagina's wordt hergebruikt (bv. navigatie, footer ...)
- Layouts: een abstracte schikking van de elementen op je website, (bv. holy grail lay-out), eventueel gebruikmakend van partials
- Pages: specifieke pagina's van je website, eventueel gebaseerd op een lay-out

Layouts: stap 1 | installatie
Installeer express-ejs-layouts
npm install express-ejs-layouts
express-ejs-layouts helpt bij het hergebruiken van een basislayout in een Express-app met EJS. Het voorkomt duplicatie van structuren, houdt de code overzichtelijk en maakt het beheren van pagina's efficiënter.

Layouts: stap 2 | configuratie
import express from "express";
import expressLayouts from "express-ejs-layouts";
// imports...
// ...
app.use(expressLayouts);
app.set("view engine", "ejs");
app.set("layout", "layouts/main");
app.set("views", "<pad-naar-views-folder>"); //
Koppel nu express-ejs-layouts
aan je gemaakte Express applicatie.
app.js

Layouts: stap 2 | configuratie
app.set("layout", "layouts/main");
Door een default layout-file te "setten", zal de inhoud steeds gerenderd worden binnen deze (default) structuur

Layouts: stap 3 | layout maken
<!DOCTYPE html>
<html lang="nl">
<head>
<!-- META TAGS -->
</head>
<body>
<%- include('../partials/navigation') %>
<%- body %>
<%- include('../partials/footer', { ...person }) %>
</body>
</html>
Maak een nieuw bestand layouts/main.ejs
📂 views/
│── 📂 layouts/
│ └── main.ejs <-- Basislayout met <%- body %>
│── 📂 pages/
│ └── home.ejs <-- Specifieke pagina die in de layout wordt geladen
Plaats de partials en een <%- body %> tag

Layouts: stap 3 | layout maken
In het layout-bestand van EJS zorgt <%- body %>
ervoor dat de inhoud van de huidige pagina (view) dynamisch wordt ingevoegd op die plek.
-
<%-
(met een streepje) betekent dat de inhoud ongeëscaped wordt gerenderd, wat betekent dat HTML-tags in de view correct worden weergegeven en niet als tekst. -
body
is de plek waar de specifieke pagina-inhoud terechtkomt binnen de layout.

Layouts: rendering flow
📂 views/
│── 📂 layouts/
│ └── main.ejs <-- Basislayout met <%- body %>
│── 📂 pages/
│ └── home.ejs <-- Specifieke pagina die in de layout wordt geladen
export const home = (req, res) => {
const data = {
title: "Yesterday is history. Today is a gift. Tomorrow is a mystery",
};
// render the home.hbs file when the /thisisatest route is accessed
res.render("pages/home", data);
};
Voorbeeld
Template structuur
app.get("/", home);
// route

Layouts: rendering flow
export const home = (req, res) => {
const data = {
title: "Yesterday is history. Today is a gift. Tomorrow is a mystery",
};
// render the home.hbs file when the /thisisatest route is accessed
res.render("pages/home", data);
};
Rendering Flow
-
Express laadt
pages/home.ejs
en verwerkt de data. -
De layout
layouts/main.ejs
wordt toegepast, waarbij
<%- body %>
wordt vervangen door de inhoud vanhome.ejs
.
- HTML wordt gerenderd en naar de client gestuurd.

Layouts: rendering flow
Diagram van de rendering structuur
┌───────────────────────────┐
│ layouts/main.ejs │
│ ┌───────────────────────┐ │
│ │ Header (vast) │ │
│ ├───────────────────────┤ │
│ │ <%- body %> │ │ ← Hier komt pages/home.ejs
│ ├───────────────────────┤ │
│ │ Footer (vast) │ │
│ └───────────────────────┘ │
└───────────────────────────┘
home.ejs
wordt ingesloten in main.ejs
op de plek van <%- body %>
, waardoor de uiteindelijke pagina een combinatie is van de layout en de specifieke pagina-inhoud

Layouts: meerdere layouts
Stel je hebt een tweede layout genaamd layouts/alternate.ejs
en je wilt deze gebruiken voor de route /special
.
// In je Express-app:
app.get("/special", (req, res) => {
res.render("pages/special", { layout: "layouts/alternate", data: someData });
});
- Globale layout: Voor andere routes zoals de homepage (
/
), zou de standaard layoutlayouts/main.ejs
worden gebruikt, zoals je hebt ingesteld metapp.set("layout", "layouts/main")
. - Specifieke layout voor de route: Voor de route
/special
wordt de layoutlayouts/alternate.ejs
gebruikt. In dit geval overschrijf je de layout alleen voor deze route.

Layouts: meerdere layouts
┌────────────────────────────────────────┐
│ layouts/main.ejs │ ← Standaard layout (voor andere routes)
│ ┌────────────────────────────────────┐ │
│ │ Header (vast) │ │
│ │ <%- body %> │ │ ← Pagina-inhoud (bijv. home.ejs)
│ │ Footer (vast) │ │
│ └────────────────────────────────────┘ │
└────────────────────────────────────────┘
┌────────────────────────────────────────┐
│ layouts/alternate.ejs │ ← Specifieke layout voor /special
│ ┌────────────────────────────────────┐ │
│ │ Custom Header for Special │ │
│ │ <%- body %> │ │ ← Pagina-inhoud (bijv. special.ejs)
│ │ Custom Footer for Special │ │
│ └────────────────────────────────────┘ │
└────────────────────────────────────────┘
Server Side Rendering met EJS
STRUCTUUR UITBREIDEN





In onze app vind je afbeeldingen van dinosaurussen. Je kunt routes gebruiken om naar een overzicht van dinosaurussen te gaan en vervolgens door te klikken naar een detailpagina.
Een scenario, waar een aparte DinoController aangewezen is.
We hebben ook gedeelde data die in beide pagina's nodig is.


Gegevens uit data.js
- Analyseer het bestand waar er json-data geëxporteerd wordt: src/data/data.js
- Haal daar de array op met gegevens over 8 dino's, met volgende properties
- id
- title
- slug
- image ( vb /assets/images/dino_03.png)
- description
- De data wordt geëxporteerd via export const dinos ={...}

DinoController maken
- Maak een DinoController aan, met daarin twee actions (a.k.a. methodes) die instaan voor afhandeling van express requests.
- De index-methode zal instaan voor het overzicht van alle dino's, de detail voor de details van elke dino
export const index = (req, res) => {};
export const detail = (req, res) => {};
- Importeer de dinosaurus-data in die controller

DinoController maken
- ...
- Importeer de dino-data in die controller
- Render voor beide actions (index en detail) andere views uit.
- Je mag de "main" lay-out hergebruiken
- Volgende stap:
- routes opvangen via app.js
(volgende slide)
- routes opvangen via app.js

Routes registreren
- In app.js beschrijf je twee GET routes,
die volgende url's opvangen-
/dinosaurs
--> naar index-methode uit DinoController -
/dinosaurs/:slug
--> naar detail-methode uit DinoController
-
/dinosaurs
- ":slug" is een route parameter (link)
- Test uit door naar de twee routes te gaan

Oefening 👨🏾💻
- Haal extra dinosaurussen op via een externe API
- https://dinoapi.brunosouzadev.com/api/dinosaurs
-
https://dinoapi.brunosouzadev.com/api/dinosaurs/:name vb
https://dinoapi.brunosouzadev.com/api/dinosaurs/aegyptosaurus - Combineer bij een request met je eigen data en render alle dino's in overzichtspagina en detail.
- Vermijd redundante code, dus zorg voor een goede data-structuur, partials, ...
- Ter info, de broncode van die API (op basis van Express met MVC) vind je hier: https://github.com/darkmoonsk/dinoapi
Server Side Rendering met EJS
HELPER METHODS


A helper function is a reusable utility that performs a specific task, making code cleaner, more modular, and easier to maintain. It simplifies complex operations and avoids repetition by encapsulating common logic.
Nuttige helpers
<h1>
Welcome to De Groene Vallei, <%= uppie(dinosaur.name) %>!
</h1>
const helpers = {
uppie: (str) => str.toUpperCase(),
lowie: (str) => str.toLowerCase(),
capitalize: (str) => str.charAt(0).toUpperCase() + str.slice(1),
truncate: (str, length) =>
str.length > length ? str.slice(0, length) + "..." : str,
};
export default helpers;
Maak een nieuw bestand utils/templateHelpers.js

Nuttige helpers
// ... imports
import helpers from "./utils/templateHelpers.js";
const app = express();
// ...
Object.assign(app.locals, helpers);
1. Importeer je helpers in app.js

2. Voeg ze toe aan de lokale context
Nuttige helpers
<h3><%= uppie(dinosaur.name) %></h3>
Gebruik
Aangezien de helper methodes in de locale context zitten, kan je ze direct gebruiken in je templates


Oefening 👩🏼💻
-
Maak een "emojify" helper die dit laat werken:
-
<%- emojify(variable, "classname") %>
- met vb volgende outputs
-
<span class="highlight">👌 T-Rex</span>
-
<span class="featured">👌 Bronto</span>
-
-
-
Maak een helper die een button maakt
-
<a class="btn" href="...">Label</a>
-
- Bedenk zelf ook een vb css(), js(), today(), ....
PGM3/3 - Server Side Rendering met EJS
By Frederick Roegiers
PGM3/3 - Server Side Rendering met EJS
- 66