Generating HTML
In classic JavaScript
function(req, res) {
let user = req.session.user;
res.send('<!Doctype html><html><head><title>Main page</title></head>'
+ '<body><h1>Hello ' + user + '</h1></body></html>');
}
Imagine writing this for every HTML page your app needs to generate…
Starting from JS version ES6
function(req, res) {
let user = req.session.user;
res.send(`
<!Doctype html>
<html>
<head>
<title>Main page</title>
</head>
<body>
<h1>Hello ${user}</h1>
</body>
</html>
`);
}
More readable, useful for small snippets, but still…
Separating views from logic
The problem
function(req, res) {
let body = '<html><head><title>Bla</title><body><table>';
for (let k in v) {
body += `<tr><td>${k}</td><td>${v[k]}</td></tr>`;
}
res.send(body + '</table></body></html>');
}
- Confusion between logic and presentation,
- Code hard to read and organize,
- Very heavy syntax (
body
variable repeated thrice), - No HTML syntax highlighting in code editors,
- Security risks (injections, etc.)…
Template languages address these problems.
Early template languages: PHP
<!DOCTYPE html>
<html>
<head>
<title>Blabla</title>
</head>
<body>
<h1>Hello <?php echo $user; ?></h1>
<?php include "content.php"; ?>
</body>
</html>
- Delimiters
<?php
…?>
introduce arbitrary executable PHP code, - Everything outside of delimiters is left as is.
Security risks: No escaping by default, too powerful.
Modern template languages
An example
<!DOCTYPE html>
<html>
<head>
<title>Blabla</title>
</head>
<body>
<h1>Hello {{ user }}</h1>
{% include "content.html" %}
</body>
</html>
- Dedicated language, distinct from platform language, less powerful:
- The value of the
user
variable is replaced for{{ user }}
; - The contents of
content.html
are injected in the output; - Everything else is output as is.
- The value of the
Template languages
Template languages typically have these features:
- Replace variables (
{{ var }}
); - Execute tests (
{% if %}
); - Loop over lists, arrays and dictionaries (
{% for %}
); - Include other templates (
{% include %}
,{% block %}
,{% extends %}
); - Chain transformations (
{{ var | upper | strip }}
); - Apply simple operators (mathematical, logical, comparisons).
Some template languages
Nunjucks (https://mozilla.github.io/nunjucks/)
Context: a dictionary of key → value associations given to the template engine. For example:
name
→"toto"
users
→["titi", "tutu", "tata"]
Variable substitution, filters
Hello {{ name }}
Hello toto
Filtres
Upper case: {{ name | upper }}
A list: {{ users | join(', ') }}
{% filter upper %}
{{ name }}
{% endfilter %}
Upper case: TOTO
A list: titi, tutu, tata
TOTO
Control
Conditionals
{% if name == 'toto' %}
Hello my dear
{% else %}
Hello
{% endif %}
Hello my dear
Loops
{% for i in range(0, 3) %}
User: {{ users[i] }}
{% endfor %}
{% for u in users %}
User: {{ u }}
{% endfor %}
User: titi
User: tutu
User: tata
User: titi
User: tutu
User: tata
Modularity
Inclusion
{% include 'other_template.html' %}
Macros
{% macro greet(name) %}
Hello Mr {{ name }}
{% endmacro %}
{% from "macros.html" import greet %}
{{ greet('toto') }}
Hello Mr toto
Inheritance
Here's the `main.html` template
Blocks are shown as is
{% block title %}
A title
{% endblock %}
{% block footer %}
Copyright Pinco Pallino
{% endblock %}
Here's the `main.html` template
Blocks are shown as is
A title
Copyright Pinco Pallino
{% extends 'main.html' %}
{% block title %}
Replaces title
{% endblock %}
Here's the `main.html` template
Blocks are shown as is
Replaces title
Copyright Pinco Pallino
Using Nunjucks in Express: res.render()
...
const app = express();
const nunjucks = require("nunjucks");
nunjucks.configure('views', {
express: app,
autoescape: true // automatic escaping
noCache: false // cache templates from filesystem
});
app.set('views', 'templates'); // look for template files are in
// 'templates' folder
app.get('/', function(req, res) {
res.render('hello.html', { name : 'Toto' });
});
Escaping
- Web programming mixes many different programming languages: HTML, CSS, JavaScript, PHP, templates, SQL, …
- Each language has its own special characters. E.g.:
<
,>
,&
,'
,"
Take this handler:
function(req, res) {
res.send(`<h1>${req.query.name}</h1>`);
}
Name:
Characters <U , Z>
are interpreted as the <U>
html tag!
HTML escaping
HTML defines escaping sequences for its special characters, called character entities.
< |
> |
& |
" |
' |
< |
> |
& |
" |
' |
These substitutions are automatically performed by
- Nunjucks, and the majority of templating engines,
- Some dedicated Node.js modules, like escape-html.
WARNING: only use for HTML!
- JSON: replace
'
→\'
,"
→\"
(in practice, useJSON.stringify()
,res.json()
), - JavaScript: like JSON, but much more carefully!
Escaping in Nunjucks
The {{ var }}
substitutions are escaped by default
Disabling escaping:
{{ name | safe }}
Re-activating escaping:
{{ name | escape }}
Name:
Nunjucks in the browser
-
Add Nunjucks to your HTML page with a
<script>
tag<script src="nunjucks.js"></script>
-
Use
nunjucks.render(template, context)
to evaluate templatesnunjucks.render('index.html', { foo: 'bar' });
See instructions at https://mozilla.github.io/nunjucks/getting-started.html
Template
Context
Document