Express and Templates

Dr. Greg Bernstein

Updated: October 25th, 2019

Express and Templates

Classic Server Side Rendering

  • Many web servers/frameworks keep site “information” in databases

  • This “information” is used to create web pages via Templates

  • This was one of the early uses of template systems in the web, other uses include static site generation, and client side rendering.

Express + Nunjucks

  • Express.js is an extremely popular JavaScript server framework.

  • Many template systems can work with Express.js

  • Nunjucks is very easy to use with Express.js

Nunjucks Template Example

const express = require('express');
var app = express();
const nunjucks = require('nunjucks');
nunjucks.configure('templates', {
    autoescape: true,
    express: app
});
let host = '127.0.3.1'; // Choose a different loopback address
let port = '7373'; // Last digits of your NetID
let myName = 'Dr. B';

let info = {host: host, port: port, name: myName}

app.get('/', function (req, res) {
    res.render('hello.html', info);
});

app.listen(port, host, function () {
    console.log("Example app listening on IPv4: " + host +
    ":" + port);
});

Template File

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Hello {{name}}</title>
</head>
<body>
  <main>
    <h1>Hi  {{name}}</h1>
      <h2>Server Running on {{host}} and port {{port}}</h2>
        <p>That's it for now</p>
  </main>
</body>
</html>

Output

Creating a Website

Multiple Uses of Templates

  1. HTML content creation from data.
  2. Just hold some static HTML content including form information
  3. Layout pages with: block substitution, site navigation, styling, etc…
  4. Unlike with static site generators we may create a lot of templates to deal with items 1 and 2.

Nunjucks

  • Provides for HTML content creation via variable substitution, loops, conditionals etc.

  • Provides for page layout via block substitution and inheritance.

Example: Galactic Wind Tours

A poorly styled site to inspire you to do better!

Create “Layout” Template

  • Want common navigation and styling across pages
  • Need different meta information title, description for different pages. Create a block for this.
  • Need different contents for each page. Create a block for this.

base.njk Template

Note block definitions and names

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        {% block metaStuff %}
            <title>Galactic Wind Tours</title>
        {% endblock %}
        <link href="tour.css" rel="stylesheet">
    </head>
    <body>
    <nav>
    <ul>
        <li><a href="/">Home</a></li>
        <li><a href="/Tours">Coming Tours</a></li>
        <li><a href="/NewsLetter">Newsletter Signup</a></li>
        <li><a href="/Login">Login</a></li>
        <li><a href="/about">About Us</a></li>
        <li><a href="/addTour">Add Tour</a></li>
    </ul>
    </nav>
    <main>
        {% block main %}{% endblock %}
    </main>
    </body>
</html>

An About Template

We create a template that extends and implements blocks in base.njk

{% extends "base.njk" %}

{% block metaStuff %}
    <title>About Us</title>
{% endblock %}

{% block main %}
    <h1 id="about-us">About Us</h1>
    <section>
    <h2 id="background">Background</h2>
    </section>

    <section>
    <h2 id="technical-stuff">Technical Stuff</h2>
    <p>Building Websites...</p>
    <p>Astronomy and Astrophysics...</p>
    </section>
{% endblock %}

Setting Up Our Server 1

Directory Structure

  • templates: directory where all templates including base.njk are kept.

  • public: directory where static assets are kept

  • .: root directory contains server program, mock data, package.json, etc…

  • node_modules: NPM packages

Setting Up Our Server 2

Initialization Code (tourServer.js)

const express = require('express');
var app = express();
app.use(express.static('public')); // static middleware
const nunjucks = require('nunjucks');
nunjucks.configure('templates', { // template directory
    autoescape: true,
    express: app
});

Serving About Page

  • Path is /about in the base.njk template.
  • All <a> links use GET method based requests
  • Handler in server:
app.get('/about', function(req, res){
    res.render('about.njk');
});

Data In Templates

  • Galactic Wind Tours offers two types of tours
  • Virtual Tours
  • Physical Tours

Sample Tour Data

{"virtTours": [
    {"name": "Kiting Neptune", "date": "Starting June 2022"},
    {"name": "Windsurfing the Methane Lakes of Titan", "date": "Starting December 2022"},
    {"name": "Kiting Jupiter's Great Red Spot", "date": "Starting June 2023"}
],

 "phyTours": [
     {"name": "Windsurf Foiling San Francisco Bay", "date": "May-September, Weekly by appointment"},
     {"name": "'Bump and Jump' Windsurfing in the South Bay", "date": "Wind watch based tour"},
     {"name": "Windsurf or Kite the Delta", "date": "Wind watch based tour"}
 ]
}

Tour Template

Renders tour data into HTML

{% extends "base.njk" %}

{% block metaStuff %}
<title>Current Tours</title>
{% endblock %}

{% block main %}
<h1 id="tours">Tours</h1>
<section>
    <h2 id="virtual-tours">Virtual Tours</h2>
    <ol>
        {%for tour in tours.virtTours %}
            <li>{{tour.name}} ({{tour.date}})</li>
        {% endfor %}
    </ol>
</section>

<section>
    <h2 id="physical-tours">Physical Tours</h2>
    <ol>
        {%for tour in tours.phyTours %}
            <li>{{tour.name}} ({{tour.date}})</li>
        {% endfor %}
    </ol>
</section>
{% endblock %}

Combining Data with Template

Server code

const tours = require('./tours.json');

app.get('/Tours', function(req, res){
    res.render('Tours.njk', {tours: tours});
});

Rendered Tours Page

Form Processing

General Approach

  • Serving the page/template with the form
  • Processing form submission
  • Provide response to form submission

Newsletter Signup Example

  • Newsletter signup path /NewsLetter renders NewsLetter.njk which has a form with action="newsSignup" with method="GET"

  • Need a handler function for path /newsSignup, GET method to process form information

  • Need NewsThanks.njk to provide a personalized response to use to thank them for signing up.

Newsletter Form Template

{% extends "base.njk" %}

{% block metaStuff %}
    <title>Newsletter Signup!</title>
{% endblock %}

{% block main %}
<h1>Galactic Wind Tours Newsletter</h1>
<section>
    <h2>Signup for our Newsletter</h2>
    <form action="newsSignup" method="GET">
    <fieldset class="newsletter">
        <legend>About You</legend>
    <div id="SignUp">
        <label>First Name</label><input type="text" name="fname"/>
        <label>Last Name</label><input type="text" name="lname" />
        <label>Email</label><input type="email" name="email"/>
        <label>How did you hear about us?</label>
        <select name="howHear">
            <option>Newpaper</option>
            <option>Facebook</option>
            <option>Radio</option>
            <option>Word of mouth</option>
        </select>
    </div>
        <button type="submit">Submit</button>
    </fieldset>
    </form>
</section>
{% endblock %}

Newletter Signup Handler

Server code to handle form submission

let newLetterSubs = []; // array to hold subscriber info
// This array would be replaced by database

app.get("/newsSignup", function(req, res){
    newLetterSubs.push(req.query);
    console.log(`News subscribers: ${JSON.stringify(newLetterSubs)}`);
    res.render("NewsThanks.njk", req.query);
});

Thank You Template

{% extends "base.njk" %}

{% block metaStuff %}
<title>Thanks for Signing up</title>
{% endblock %}

{% block main %}
<h1 id="about-us">Thanks for Signing up</h1>
<section>
    <h2>Subscribed</h2>
    <p>Thanks {{fname}} {{lname}} you've been subscribed to our
        newsletter.</p>
    <p>Also confirming that you heard about us from <em>{{howHear}}</em></p>

</section>
{% endblock %}

Rendered Signup Form

Rendered Thank You