Server Side Rendering met EJS
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
En gebruik dan steeds nodemon (ipv node) om je
Node-applicatie te starten
// package.json
{
// ...
"scripts": {
// ...
"start": "nodemon index.js"
}
}
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"
}
}
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
// package.json
{
// ...
"scripts": {
// ...
"start": "nodemon --env-file=.env ./src/app.js"
}
}
Het start script in package.json zou er dus zou kunnen uitzien
Omgevingsvariabelen definieer je vervolgens in een
.env file
// .env
PORT=3000
De library dotenv heb je niet meer nodig sinds node v20.6.+
Plaats .env altijd in
.gitignore
# General
.cache
package-lock.json
# OS specific
.DS_Store
# IDE
.vscode
.idea
# Node
node_modules
.env
.env-example
toont welke omgevingsvariabelen nodig zijn..env
bestand in te stellen.Server Side Rendering met EJS
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.
Installeer EJS
npm install 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>
import express from "express";
// imports...
// ...
app.set('view engine', 'ejs');
app.set("views", "<pad-naar-views-folder>"); //
export const home = (req, res) => {
res.render('home');
};
app.get('/', home);
Server Side Rendering met EJS
<h2><%= user.name %></h2>
<% if (user) { %>
<h2><%= user.name %></h2>
<% } %>
{
person: {
firstname: "Yehuda",
lastname: "Katz",
},
}
<%= person.firstname %> <%= person.lastname %>
<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>
<%= %>
: 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!" }) %>
:<% %>
-blokken.__append("...")
__append("...")
?__append("...")
voegt HTML of tekst rechtstreeks toe aan de uitvoer binnen een EJS-codeblock<% %>
-blokken, waar normaal geen directe uitvoer mogelijk is.<%= %>
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("...")
__append("...")
handig?Gebruik het slim, maar met mate!
Te veel __append()
kan je code minder leesbaar maken.
⚠ Niet geschikt voor grotere HTML-structuren (minder leesbaar).
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>
<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.<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
EJS allows for template reuse through includes
(often referred to as "partials").
Partials are normal templates that may be called directly by other templates.
<%- include('partials/yourFileName.ejs') %>
<%- 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.
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
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.
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
app.set("layout", "layouts/main");
Door een default layout-file te "setten", zal de inhoud steeds gerenderd worden binnen deze (default) structuur
<!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
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.
📂 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
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
pages/home.ejs
en verwerkt de data.layouts/main.ejs
wordt toegepast, waarbij<%- body %>
wordt vervangen door de inhoud van home.ejs
.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
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 });
});
/
), zou de standaard layout layouts/main.ejs
worden gebruikt, zoals je hebt ingesteld met app.set("layout", "layouts/main")
./special
wordt de layout layouts/alternate.ejs
gebruikt. In dit geval overschrijf je de layout alleen voor deze route.┌────────────────────────────────────────┐
│ 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
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.
export const index = (req, res) => {};
export const detail = (req, res) => {};
Server Side Rendering met EJS
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.
<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
// ... 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
<h3><%= uppie(dinosaur.name) %></h3>
Gebruik
Aangezien de helper methodes in de locale context zitten, kan je ze direct gebruiken in je templates
<%- emojify(variable, "classname") %>
<span class="highlight">👌 T-Rex</span>
<span class="featured">👌 Bronto</span>
<a class="btn" href="...">Label</a>