Tutorial

How To Build SPAs (Single Page Applications) using NodeJS, SailsJS and DustJS in Ubuntu 14.04

Published on May 27, 2015
How To Build SPAs (Single Page Applications) using NodeJS, SailsJS and DustJS in Ubuntu 14.04

Introduction

Node.js® is a platform built on Chrome’s JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices.

Sails is a NodeJS framework for the backend server. Based on a Model-View-Controller pattern, it allows for rapid development of applications. Sails has built-in integration for web sockets for realtime push messages. It uses Waterline ORM as the default ORM, making it database-agnostic. In other words, it allows for database operations across a spectrum of SQL as well as non-SQL databases. Most importantly, Sails provides a proper structure to your application.

Dust is a JavaScript template engine. It inherits its look from the ctemplate family of languages and is designed to run asynchronously on both the server and the browser.

SPA stands for Single Page Application. These are applications that fit on a single webpage. Once the site opens, the page does not reload thereafter. The goal of such an application is to provide a fluid user experience by cutting down page load time and providing easy transitions to different pages, just like desktop apps.

In this tutorial, we will set up a NodeJS server with SailsJS as the framework to manage our code. We will use DustJS for isomorphic templates used on both the client and the server.

Goals

Our main aim is to use Isomorphic (or same) templates on the client as well as on the server. This is awesome because

  • Less code to write and maintain as same templates on both client and server
  • Search engine indexable as any page can be fetched directly from the server

Prerequisites

This tutorial assumes that you have created an Ubuntu 14.04 x64 Droplet. It was tested with 512 MB of RAM. You will also need a user with sudo access to install the packages.

Step 1 — Installing the Software

First, to compile and install native addons from npm you need to install build tools:

  1. sudo apt-get install python-software-properties python g++ make

Then, install NodeJs and NPM using the following commands (from Chris Lea’s PPA):

  1. sudo add-apt-repository ppa:chris-lea/node.js
  2. sudo apt-get update
  3. sudo apt-get install nodejs

Note: This also installs the Node Package Manager or npm. NPM is used to install node packages. Its like the apt-get for NodeJs. We’ll use it to install node modules like Sails, dust-compiler, and other requirements.

Next, install SailsJs:

  1. sudo npm -g install sails

Note: This installs the latest version of Sails. You can read more about Sails.js on their website.

Step 2 — Creating a New Sails Project

Create a new application:

  1. sails new dustspa
  2. cd dustspa

Sails will create the dustspa directory with the following structure:

--config
--views
---api
--tasks
--assets
README
.gitignore
package.json
.sailsrc
app.js
Gruntfile.js

Find the following lines in the package.json file:

"dependencies": {
"sails": "~0.11.0",

Change them to the following:

 "dependencies": {
     "dustjs-linkedin": "^2.5.1",
     "sails": "~0.11.0",

The dust compiler is used to compile dust templates to dust JavaScript templates.

Next, install the dustjs packages as well as the other package dependencies in the package.json file:

  1. sudo npm install

Now, we use sails lift to lift the server:

  1. sails lift

Visit your_server_ip:1337 to view the default home page. Stop the server using Ctrl+C when done.

Step 3 — Setting the View Engine

First, we set the View Engine to dust:

In config/views.js, change engine: 'ejs' to engine: 'dust':

  1. nano ./config/views.js

Go to line:

    engine: 'ejs',

and change it to:

    engine: 'dust',

After the changes are done, press Ctrl+X (save), Y (confirm file name), and ENTER (save and exit).

We will use the above method to edit all the files in future.

Step 4 — Creating layout.dust

The content from layout.dust will be our landing page. All requests will come to this page for the first page. After that, requests will be handled at the frontend.

Create file layout.dust in the views folder:

  1. touch views/layout.dust

Copy the following HTML code into layout.dust:

layout.dust
<!DOCTYPE html>
<html>
<head>
  <title>Dust SPA</title>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
</head>
<body>
  <div class="container">
    <div class="header">
      <nav>
        <ul class="nav nav-pills pull-right">
          <li role="presentation" class="active"><a href="#" class="links" data-template="home">Home</a></li>
          <li role="presentation"><a href="#" class="links" data-template="about">About</a></li>
          <li role="presentation"><a href="#" class="links" data-template="contact">Contact</a></li>
        </ul>
      </nav>
      <h3 class="text-muted">SPA - DustJS Templates, SailsJS Architecture</h3>
    </div>
    <br/>
    <div id="template">
      <h1>Partial Goes Here</h1>
    </div>
    <footer class="footer">
      <p>Styled by Bootstrap&copy;</p>
    </footer>
  </div>
</body>
</html>

The HTML is pretty simple. It uses:

  • Bootstrap for styling.
  • A simple nav for navigation links.
  • <div id="template">...</div> where our templates will load

Next, we need to set the view for / in routes.js. Open config/routes.js and edit it to:

routes.js
'/': {
   view: 'layout'
    }

Now, let’s lift the server (If the server is already up, press Ctrl+C to stop it.), and check our progress. Run this command from the dustspa directory (which is our currect directory) :

  1. sails lift

And point your browser to the following location: droplet_ip:1337

If you can see the page, then let’s proceed. If not, please review previous steps. Also, the links will not work at this point.

Step 5 — Creating Partials

Note: Partials (or templates) are a part of a page. In SPA, we do not replace the whole page, just the partial.

Create the directory partials in views directory, and change to this new directory:

  1. mkdir views/partials
  2. cd views/partials

Create the home.dust, about.dust, and contact.dust files in the partials directory with the following content:

home.dust
    <div class="jumbotron">
      <h1>Home Page
        {?home}<small class="pull-right">Visit Count: {home}</small>{/home}
      </h1>
      <p class="lead">This is the home page. Its actually a dust partial which will be replaced when we navigate away from the page. Only this partial will change while the rest of the page will not be reloaded. SPA Magic!</p>
      <p>
        <a class="btn btn-lg btn-success" href="http://linkedin.github.io/dustjs/" target="_blank" role="button">Check out DustJS</a>
        <a class="btn btn-lg btn-warning pull-right" href="http://sailsjs.org" target="_blank" role="button">Check out SailsJS</a>
      </p>
    </div>
about.dust
    <div class="panel panel-primary">
      <div class="panel-heading">
      <h1 class="panel-title">About Us
        {?about}<kbd class="pull-right">Visit Count: {about}</kbd>{/about}
      </h1>
      </div>
      <div class="panel-body">
        <h4>This is the About Us template. Its actually a dust partial which will be replaced when we navigate away from the page. Only this partial will change while the rest of the page will not be reloaded. SPA Magic!</h4>
        <br>
        <p>
          <a class="btn btn-lg btn-success" href="http://linkedin.github.io/dustjs/" target="_blank" role="button">Check out DustJS</a>
          <a class="btn btn-lg btn-warning pull-right" href="http://sailsjs.org" target="_blank" role="button">Check out SailsJS</a>
      </p>
      </div>
    </div>
contact.dust
    <div class="well">
      <h1 class="align-center">Show us some love and we'll get back to you !
        {?contact}<small class="pull-right">Visit Count: {contact}</small>{/contact}
      </h1>
      <hr/>
      <div class="input-group input-group-lg">
        <span class="input-group-addon" id="basic-addon1">@</span>
        <input type="text" class="form-control" placeholder="Email" aria-describedby="basic-addon1">
      </div>
      <br/>
      <p>
      <a class="btn btn-lg btn-success" href="#" role="button">Send Contact</a>
      </p>
    </div>

Step 6 — Compiling Templates

Dust templates need to be compiled before they can be used on the frontend.

Let’s install the dust compiler:

  1. sudo npm install -g dust-compiler

We will keep all our compiled templates in assets/templates. Change to the main dusts directory:

  1. cd ../..

Now, compile the dust templates:

  1. dust-compiler -s views/partials/ -d assets/templates/ --bootstrap --nonotify

Now, you should have three files in your assets/templates folder:

  1. home.js
  2. about.js
  3. contact.js

Now, we have all the required files.

First, in views/layout.dust, add the dust-js libraries and template files inside the <body> tags at the very end:

layout.dust
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/dustjs-linkedin/2.5.1/dust-core.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/dustjs-helpers/1.5.0/dust-helpers.min.js"></script>

<script type="text/javascript" src="/templates/home.js"></script>
<script type="text/javascript" src="/templates/about.js"></script>
<script type="text/javascript" src="/templates/contact.js"></script>

Having done that, we need to add a script to capture link clicks and render the required template. Create assets/js/clickHandler.js with the following content:

clickHandler.js
    (function () {

  var links = document.getElementsByClassName("links"),
  templateDiv = document.getElementById("template"),
  nav = document.querySelector('nav > ul'),
  clicked = false, viewCount = { home: 2, about: 1, contact: 1 };

  function clickHandler(e) {
    var target = event.target,
    templateName = this.getAttribute("data-template");
    if(clicked) {
      clicked.removeAttribute('class');
    }
    else {
      nav.querySelector('.active').removeAttribute('class');
    }
    target.parentElement.setAttribute('class','active');
    clicked = target.parentElement;

    dust.render(templateName, viewCount, function (err, out) {
      if(err) console.log('Error:',err);
      else {
        viewCount[templateName]++;
        templateDiv.innerHTML = out;
      }
    });
  };

  for(var i = 0; i < links.length; i++){
    links[i].addEventListener('click', clickHandler, false);
  }

})();

The above script is very simple. It does the following:

  • Captures click events on links
  • Extracts template name for a link from the data-template attribute
  • Stylizes the clicked link
  • Renders the template using the dust.render function and pass an object viewCount (could contain anything, here it contains the count of view access)

Reference this new clickHandler.js file by editing the views/layout.dust and adding the following inside the <body> tags at the very end:

 <script type="text/javascript" src="/js/clickHandler.js"></script>

Finally, we need to edit layout.dust in the views directory.

Change the <div id="template">...</div> in views/layout.dust to:

layout.dust
<div id="template">
  {> "partials/home"/}
</div>

What does the above change do?! This is Template Reuse. The dust.js view engine will replace {> "partials/home"/} with the contents of the file view/partials/home.dust.

How will this template be reused? A compiled version of this template (that we compiled using dust-compiler) resides in assets/templates/home.js. This compiled template will be included in a script tag later on. Once a template is included using a <script src="/templates/home.js"/> tag, it (the template) is automatically added to the dust.cache object in the frontend. Now, to re-render this template, we use dust.render("home", obj, callbackFunction). You can check the code in the assets/js/clickHandler.js for a better understanding of the code.

A Dust template named xxx is authored in a file named xxx.dust. You can have multiple .dust files and reference one dust.js template as part of another one. This is the basis for “components” or reusable templates for tasks like a common header and footer on multiple pages. Note that the .dust file extension is used here in examples, but .tl is also commonly seen. Since it only matters to the build process, you can use whatever extension works for you.

The partial reference syntax {> name /} also supports paths so you can have a template at a path like shared/header.dust and reference it as {> "shared/header" /}. This allows partials to be organized into library-like structures using directories.

Read more about Partials on github.

Your final layout.dust should look like this:

layout.dust
<!DOCTYPE html>
<html>
<head>
  <title>Dust SPA</title>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
</head>

<body>
  <div class="container">
    <div class="header">
      <nav>
        <ul class="nav nav-pills pull-right">
          <li role="presentation" class="active"><a href="#" class="links" data-template="home">Home</a></li>
          <li role="presentation"><a href="#" class="links" data-template="about">About</a></li>
          <li role="presentation"><a href="#" class="links" data-template="contact">Contact</a></li>
        </ul>
      </nav>
      <h3 class="text-muted">SPA - DustJS Templates, SailsJS Architecture</h3>
    </div>
    <br/>
    <div id="template">
      {> "partials/home"/}
    </div>
    <footer class="footer">
      <p>Styled by Bootstrap&copy;</p>
    </footer>
  </div>

  <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/dustjs-linkedin/2.5.1/dust-core.min.js"></script>
  <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/dustjs-helpers/1.5.0/dust-helpers.min.js"></script>
  <script type="text/javascript" src="/templates/home.js"></script>
  <script type="text/javascript" src="/templates/about.js"></script>
  <script type="text/javascript" src="/templates/contact.js"></script>
  <script type="text/javascript" src="/js/clickHandler.js"></script>
</body>

</html>

Step 7 — Testing the App

Lift the server:

  1. sails lift

Point your browser to the following: your_server_ip:1337

Test SPA by clicking the top 3 links: Home, About, Contact

Conclusion

Awesome ! We successfully built an isomorphic website that allowed same template use on both client and server. We built a Single Page Application (SPA) Website and also got an understanding of using the SailsJS framework.

If you want to read more about the technologies used, you could visit the links listed below.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the authors
Default avatar
myusuf91

author


Default avatar
Tammy Fox

editor


Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
7 Comments


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

With 0.12 I get a log “info: Could not find module: dust in path:” whenever i lift the sails app. Do you have a solve for this?

Can use pug for the templates?

Love the article. I’ve just started to learn sails as I’ve come from a c# mvc background and wanted to try node. Single page applications seem like fun to work with. As suggested an article showing links to a db utilising sails and updating the templates would be good. Infact I’d be really interested to see how Web sockets come into play with dust and sails.

Great article! I would love to see a ‘Part II’ with MongoDB included, and examples on how to transfer and modify data from backend to frontend and vice versa, in this particular context.

In my views/partials/home.js I got an error … dust is not defined; please fix or add /global dust/ Adding …

var dust = require(‘dustjs-linkedin’);

before the function(){ … }

ref this http://www.dustjs.com/guides/setup/

Wanted to post in case someone else comes up with this issue.

Hello myusuf91, Please can you help me to installing node.js and writting an apllication with node from scratch with ubuntu OS, i am completely new to nodejs.

How install project to folder /var/www/example.com/public_html ?

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Featured on Community

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more