Templating
Exercise: Let's change the text for the link, Contact, to Contact Information.
- How many files do you have to change? Wouldn't be nice if we could change one file and that reflects in all pages.
At the moment, we're just serving static HTML files from the public folder. NodeJS and ExpressJS allows us to build dynamic web pages.
A server-side dynamic web page is a web page whose construction is controlled by an application server that processes server-side scripts. In server-side scripting, parameters determine how the assembly of every new web page proceeds, including the setting up of more client-side processing.
Websites - a small detour
What is wrong with this website?
Is this one any better?
The final view is here
What was missing in the first example?
So what is the point of all these?
Template Engines
A template engine enables you to use static template files in your application. At runtime, the template engine replaces variables in a template file with actual values, and transforms the template into an HTML file sent to the client. This approach makes it easier to design an HTML page, as it reduces duplicate code (DRY).
Some popular template engines that work with Express are Pug, Mustache, and EJS.
Handlebars Templating Engine
We will use Handlebars as the templating engine for this class.
To add it to your project:
npm install --save express-handlebars
Then in your server.js
, we need to configure the middleware
for Handlebars.
// Add this to the top of your file
const exphbs = require('express-handlebars');
// Then these two lines after you initialise your express app
app.engine('handlebars', exphbs({ defaultLayout: 'main' }));
app.set('view engine', 'handlebars');
Let's look at the folder
views/layouts/main.handlebars
- There is a placeholder for body where the "body" get injected - can you find it?
- Notice the index.handlebars, my-cv.handlebars and admin.handlebars.
Then add these routes:
app.get('/', function (req, res) {
res.render('index');
});
Exercise: Add similar endpoints for
/my-cv
and/admin
.Exercise: Let's create a template called
contact
and delete the endpoint we created earlier for/contact
- render a view instead - similar to what we did with the previous endpoints.Exercise: Remember how we changed Contact to Contact Information? Try to change now Contact Information to Get in touch. Was it any easier?
Template passing info from Controller to Template
When we were using the static HTML pages from public/ folder - the heading text shown on top of the page (on the photo) used to change when we go to different pages. Now that we switched to the template, we've lost that behavior as the code for this part comes from the centralised Layout (views/layouts/main.handlebars).
How can we imitate that behavior without having to duplicate code or serve static files? Express and Handlebars allow you to pass data between the routes and the views. Here is how we can do it:
Let's modify the
/
route to pass the title of the page.app.get('/', function (req, res) { res.render('index', { title: 'Etzali Profile', // insert your name instead }); });
In
/views/layouts/main.handlebars
, let's use the data we're passing to the template<div class="row"> <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1"> <div class="site-heading"> <!-- Notice the change in the next line --> <h1></h1> <hr class="small"> <span class="subheading">A modern Website built in Node</span> </div> </div> </div>
Exercise: Pass the
subheading
from the route to the view as well.Exercise: Change Title to default to "My Profile" if no title was provided. Look at #if helpers for Handlebars http://handlebarsjs.com/builtin_helpers.html
Partials
Handlebars allows for template reuse through partials. Partials are normal Handlebars templates that may be called directly by other templates. (Handlebars documentation)
Let's put the Menu in a partial.
- Go to
layout/main.handlebars
and cut the<nav>
with all its contents, and move it to a new file calledmenu.handlebars
underviews/partials
.
Then to use the partial, add {{> menu}}
in the place where you cut the original menu from.
More Templating
There is much more that can be done with Handlebars templating engine. Skim through the express-handlebars and handlebars documentation
For now, we will finish by using the each helper.
Dynamically loading the templates
{{#each posts}}
<div class="post-preview">
<a href="post.html">
<h2 class="post-title">
{{this.title}}
</h2>
<h3 class="post-subtitle">
{{this.summary}}
</h3>
</a>
</div>
{{/each}}
In the route
, let's load the file in data/posts.json
:
app.get('/', function (req, res) {
const filePath = __dirname + '/data/posts.json';
const callbackFunction = function(error, file) {
// we call .toString() to turn the file buffer to a String
const fileData = file.toString();
// we use JSON.parse to get an object out the String
const postsJson = JSON.parse(fileData);
// send the json to the Template to render
res.render('index', {
title: 'Etzali Profile', // insert your name instead
posts: postsJson
});
};
fs.readFile(filePath, callbackFunction);
});