January 29, 2013
Single Page App with Twitter Bootstrap and Node.js

This is Part 2 of a 3 part series on a single page application for Node.JS.


In Part 1, I reviewed the server-side, and you got to see how the routes are set-up in a typical modular Node.js app using the Express Framework.

Part 2 of my single-page app lesson covers the UI (presentation) layer including the EJS (Embedded Javascript) templates and rendering.

There are several template engines for Node such as Jade, EJS, HAML and Hogan. I decided to go with EJS since it lets us define templates using familiar HTML tags.

Things have changed slightly on EJS configuration from Express 2.x to Express 3.x. The layout file (master template used as a wrapper) must be explicitly set in app.locals, and we must “require” ejs-locals module to use the layout file in Express 3.x.

Using EJS

In app.js, EJS is configured as the template engine:

		app.set('views', __dirname + '/views');
		app.set('view engine', 'ejs');

	app.engine('ejs', require('ejs-locals'));

The ‘layout.ejs’ file is the wrapper template for the single-page app. This includes the HTML doctype, HEAD, META tags, links to any Javascript and CSS files that are used. The layout also includes any app-level scripts that we want to reference. The scripts are used to control elements such as the dot navigation, scrolling effect and alerts.

The layout.ejs template contains a <%- body %> tag which is where EJS will render content from the page-level ‘index.ejs' template.

<!DOCTYPE html>
<html lang="en">
  <head> .. </head>
  <body>..<%- body %>..</body>

When a server-side request is routed, the response contains a Javascript object that is used to pass data from the Node server to EJS. The values in the Javascript object can be reference anywhere we need to render data (content). There are basically 3 ways to reference data values in the EJS templates:

<%- value %> - Unescape HTML for literal rendering of HTML or script.

<%= value %> - Escaped HTML for standard HTML rendering of content.

<% code %> - Is used for Javascript code blocks.

For example, consider the Javascript object sent by the default single page ‘/’ route. The object is passed to the ‘index.ejs’ template using res.render in Express:

res.render('index.ejs', {locals:locals,sections:sectionsDb,app:appDb.get('app'),page:appDb.get('page'),msg:req.query.msg,err:req.query.err});

Rendering app-level data in layout.ejs:

The ‘app’ Javascript object contains app-level data loaded from the appDb. The ‘app’ object looks like:

 "title":"Single page app",
 "keywords":"one page,single page,app,bootstrap,mobile",
 "description":"PortalApp is a demo of the single page app."

So we can render these values in the ‘layout.ejs’ template using:

<title><%= app.title %></title>
<meta name="description" content="<%= app.description %>">
<meta name="keywords" content="<%= app.keywords %>">

Rendering section data in index.ejs:

The ‘sections’ object (loaded by the server from the ‘sectionsDb’ database) contains a Javascript array with content for each section to display on the page. Each section object contains a unique key and value. The value object contains several properties (heading,content,etc..) with the actual content to be rendered. In the template we can use dot-notation (sections[0].value.content) to reference values in the object.

Here is the logic in the ‘index.ejs’ template that iterates the ‘sections’ array to display each section of content:

<% sections.forEach( function( key,val ){ %>
<% if (val && val.enabled=="1"){ %>
<section >
    <% if (val.heading){ %><h2><%- val.heading %></h2><% } %>
    <%- val.content %>
<% } %>
<% }); %>

As you can see above a .forEach loop is used to get each key and val from the sections array. Then we check to make sure that the section is enabled (val.enabled==1), before displaying val.heading and val.content.

In the next Part 3, I’ll take a look at the Administration tools that enable content management in the single-page app.

Part 1 | Demo | Download

  1. nickleefly reblogged this from in1blog
  2. in1blog posted this
Twitter: @in1_
Facebook: in1dotcom