Templates

Dr. Greg Bernstein

Updated February 18th, 2020

JavaScript Templates via Nunjucks

References

What Are They?

A way of mixing code with text or a markup language to produce custom documents.

They may be specific to a particular programming language, e.g., JavaScript, Python, Ruby, and may be specific or independent of a particular markup language, e.g., HTML.

Where Are they Used?

  • Server side HTML page rendering

  • Static site Generation

  • Client side HTML page rendering

Examples

  • The Mako python template engine is used by reddit.com where it delivers over one billion page views per month.

  • Metalsmith and Wintersmith are examples of two modern Node.js static site generators that utilize templates.

  • The popular Angular and Vue front end frameworks utilize templates.

Issues

  • Too many choices of template engines

  • Wide variety of syntax

  • We’ll restrict ourselves to some basic features of one of the “popular” template engines that has multiple language implementations.

Nunjucks/Jinja2

  • We will use a JavaScript derivative of the Jinja2 Python templating language called Nunjucks.

  • Both are independent of the final markup language (HTML, Markdown, text, etc…)

Features I

  • Variable substitution {{blah}} where blah is any JavaScript variable.
  • Variable creation and modification
  • Filters are functions that can be applied to variables

Features II

  • Template inheritance via extends and block
  • Control: if, for
  • Includes: include other files within a template

Example Files

Get NunjucksTemplateExamples1.zip

Example Template

From the examples /views/hello.njk:

<main>
  <h1>Hello {{name}}</h1>
  <h2>Welcome to {{class}}</h2>
  {% if teacher %}
  <h3>Have fun <strong>teaching!</strong></h3>
  {% else %}
  <h3>Have fun learning all about <em>Web Systems!</em></h3>
  {% endif %}
</main>

Data for the Template

From examples user.json:

{
    "name": "Dr. B",
    "class": "CS651",
    "teacher": true
}

Node.js JavaScript to Run Nunjucks

From examples fileRender1.js used to run the template engine

const nunjucks = require('nunjucks');
const user = require('./user.json');
const fs = require('fs');  // The file system module
// Tells nunjucks where to look for templates and set any options
nunjucks.configure('views', { autoescape: true });
let outString = nunjucks.render('hello.njk', user);
fs.writeFileSync('./output/hello.html', outString);
console.log("Wrote file");

Result

Will be found in examples /output/hello.html

<main>
  <h1>Hello Dr. B</h1>
  <h2>Welcome to CS651</h2>
  <h3>Have fun <strong>teaching!</strong></h3>
</main>

Using Nunjucks with Node.js

Installation

  • npm install nunjucks

Simplest Test Possible

from examples test1.js

nunjucks = require('nunjucks');  // Import Nunjucks
nunjucks.configure({autoescape: true});
// Give it a template in string form, and a JS Object with info
let helloStr = nunjucks.renderString('Hello {{username}}',
    {username: 'Dr. B.'});
console.log(helloStr);

Additional Functionality

Given raw website content resources you may want to manipulate them in various ways to turn it into web pages

  • Control Flow and programming: if, for, comparisons, logic…
  • Filtering: content manipulation
  • OOP: Inheritance for reuse of templates.

Conditional Rendering

Check a variable to determine what to render

{% if hungry %}
  <p>Time to eat!</p>
{% elif tired %}
  <p>Time to sleep!</p>
{% else %}
  <p>Time to code or windsurf!</p>
{% endif %}

Rendering Many Pages from Data

  • Given a large data set we may want to generate many pages from that data.
  • In the “old days” we might call this a “report generator”
  • Example: Create pages of Nobel prizes over the years grouped into categories such as Physics, Chemistry, Medicine, etc…
  • Link all these pages together for easy navigation

Iterative Rendering Template

From example view/prizes.njk

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Nobel Prizes in {{catDate.title}}</title>
  <link rel="stylesheet" href="prizes.css">
</head>
<body>
  <main>
    <h1>Nobel Prizes in {{catDate.title}}</h1>
      <h2>From {{catDate.start}} to {{catDate.end}}</h2>
      <nav>
          <p> Other categories:
          {% for item in menu %}
          <a href="{{item.fname}}">{{item.title}}</a>
          {% endfor %}
          </p>
      </nav>
<table>
    <thead><tr>
        <th>Year</th>
        <th>Name</th>
        <th>Accomplishment</th>
        </tr></thead>
    <tbody>
        {% for prize in prizes %}
            {% for laureate in prize.laureates %}
        <tr>
            <td>{{prize.year}}</td>
            <td>{{laureate.firstname}} {{laureate.surname}}</td><td>{{laureate.motivation}}</td>
        </tr>
            {% endfor %}
        {% endfor %}
    </tbody>
      </table>
  </main>
</body>
</html>

Raw Data

Data from https://www.nobelprize.org/

{
    "prizes": [{
        "year": "2017",
        "category": "physics",
        "laureates": [{
            "id": "941",
            "firstname": "Rainer",
            "surname": "Weiss",
            "motivation": "\"for decisive contributions to the LIGO detector and the observation of gravitational waves\"",
            "share": "2"
        }, {
            "id": "942",
            "firstname": "Barry C.",
            "surname": "Barish",
            "motivation": "\"for decisive contributions to the LIGO detector and the observation of gravitational waves\"",
            "share": "4"
        }, {
            "id": "943",
            "firstname": "Kip S.",
            "surname": "Thorne",
            "motivation": "\"for decisive contributions to the LIGO detector and the observation of gravitational waves\"",
            "share": "4"
        }]
    }, {
        "year": "2017",
        "category": "chemistry",
        "laureates": [{
            "id": "944",
            "firstname": "Jacques",
            "surname": "Dubochet",
            "motivation": "\"for developing cryo-electron microscopy for the high-resolution structure determination of biomolecules in solution\"",
            "share": "3"
        }, {
            "id": "945",
            "firstname": "Joachim",
            "surname": "Frank",
            "motivation": "\"for developing cryo-electron microscopy for the high-resolution structure determination of biomolecules in solution\"",
            "share": "3"
        }, {
            "id": "946",
            "firstname": "Richard",
            "surname": "Henderson",
            "motivation": "\"for developing cryo-electron microscopy for the high-resolution structure determination of biomolecules in solution\"",
            "share": "3"
        }]
    }]
  }

“Driver” Code

Produces a set of Nobel prize pages based on a list of categories and date ranges. See examples NobelPrizes.js

/* A file to rendering Nobel Prize information from JSON into HTML
with *nunjucks*.
*/

const nunjucks = require('nunjucks');
const prizesJSON = require('./prize.json');
const fs = require('fs'); // The file system module

let catDates = [{
        topic: "physics",
        start: 1926,
        end: 1954,
        title: "Physics",
        fname: "physics.html"
    },
    {
        topic: "chemistry",
        start: 1936,
        end: 1964,
        title: "Chemistry",
        fname: "chemistry.html"
    },
    {
        topic: "economics",
        start: 1940,
        end: 1980,
        title: "Economics",
        fname: "economics.html"
    }];


nunjucks.configure({
    autoescape: true
});
nunjucks.configure('views', {
    autoescape: true
});

// Process prizes.json to get only information of interest
let prizeArray = prizesJSON.prizes;
for (let catDate of catDates) {
    let myPrizes = prizeArray.filter(function (x) {
        return (x.category === catDate.topic) && (x.year >= catDate.start) && (x.year <= catDate.end)
    });
    myPrizes.reverse();
    let prizeInfo = {
        catDate: catDate,
        prizes: myPrizes,
        menu: catDates
    }
    // console.log(JSON.stringify(myPrizes));


    let outString = nunjucks.render('prizes.njk', prizeInfo);
    fs.writeFileSync('./output/' + catDate.fname, outString);
    console.log("Wrote file " + catDate.fname);
}

Running the Script

  • node NobelPrizes.js
  • Look in /output directory in examples to see the generated HTML files

Where/When to Create the HTML?

  • HTML (pages) are created ahead of time and stored on server which waits for a page request. Static Site

  • HTML is created in Web Browser via the DOM and JavaScript from data from website. Web App

  • HTML is created on the server when a request comes in based on data stored on the server. Classic Website, Server side rendering, e.g., WordPress, Drupal, etc…