Template languages Django and friends

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.

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.

&lt; &gt; &amp; &quot; &apos;
< > & " '

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, use JSON.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 templates

    nunjucks.render('index.html', { foo: 'bar' });
    

See instructions at https://mozilla.github.io/nunjucks/getting-started.html

Template

Context

Document

References

Fork me on GitHub