Toy City 2

Employee Website – a NodeJS example

This is going to build off of the last tutorial. If you haven’t gone through it, you’ll need to – the MongoDB portions are referenced in this tutorial.

Alright, so, let’s get started.

There are several files that we need to have, and two sub-folders that we’re going to put them in. But first, you need to make sure MongoDB is running and ready to accept connections. You can do that with the following commands in Windows:

cd c:/program files/mongodb/server/3.2/bin

mongod

You’ll see a bunch of text scroll across the screen, and then you can set this command prompt window aside.

Next, we’ll need the following files created:

  • app.js
  • views/home.html
  • views/results.html
  • views/search.html
  • routes/routes.js

This will be the basic structure of the site. The views are what we will display to the users, the routes detail URL routes and what should happen when they are seen, and app.js is the basic server and miscellaneous other features.

We’re going to get started in App.js – we have to get our packages ready to go.

var express = require('express');
var MongoClient = require('mongodb').MongoClient;
var url = 'mongodb://localhost:27017/Users';
var engines = require('consolidate');
var app = express();

We’ve used all of these before. A quick recap: Express lets us quickly create our URL’s. Mongodb allows us to retrieve data from our database. Consolidate allows us to access Nunjucks, which is going to be our view engine.

app.engine('html', engines.nunjucks);
app.set('view engine', 'html');
app.set('views', __dirname + '/views');

This sets our engine to use html files by default, and to display them using the nunjucks engine. It also tells our app where to look for views.

Now, we want to get data from MongoDB. We know we’re going to need to pass the data back to an object that we can use in our templates. Because of the asynchronous way that NodeJS works, we’re going to need to use callbacks. So the method/variable below will have a callback built into it. I’ll walk you through each line.

var getDataFromMongoDB = function(query, callback) {
  MongoClient.connect(url, function(err, db) {
    db.collection('UserInformation').find(query).toArray(function(err, docs) {
      callback(docs);
    });
  });
}

So, the first thing you’ll notice is that our getDataFromMongoDB function has a query parameter. That’s representing a json object that we will build later. Suffice it to say, it’s going to be our criteria for getting data from the database. Alright, so, we know we want to collect the documents in an array. (We will go more into why when we get to the templates, but an array is the best way to hold this data.) Luckily, we’re going to use the ToArray function to get us that array. Well get into that in a second. First, we need to call MongoClient.Connect() to connect to the MongoDB. The URL was defined earlier, and we then pass a callback function that will trigger automatically after the connection has been made.

For this callback, an error object always has to be provided first. If an error occurs, it will be found inside this object. There are ways of checking for errors, and they should be used for any production product. But for now, we’re going to ignore them so we don’t get lost in the weeds. The second object will always be the initialized DB object that we received when we connected.

Inside the callback, we use ‘find’ to find data that matches our query object inside the ‘UserInformation’ collection. (This was created and set up in the last tutorial.) We then pass that information to an array object by using ToArray (this automatically consumes the cursor object that is returned when a query is done on the database. We pass it a callback function, (which also takes an err first, and then passes an array of whatever data find() was able to find.)

Luckily for us, this data is already in an array, so we just need to call our Callback Function and pass it the object so that it can pass the data back up the chain. That’s it. We’ve got our database call, easy and ready to work. (You’ll notice this is significantly different than the last MongoDB connection I did in the last tutorial. I learned some stuff between that one and this one, and that’s reflected in this tutorial.)

Now, for the moment, we’re done with our app.js file. We need to create some of our other files, and figure out what we want those files to do. Let’s start with our routes file.

In the routes file, we’re going to need to have routes for the following:

  • Males
  • Females
  • First Name
  • Last Name
  • ID
  • Default

For the Names and ID, they will also need to accept a search parameter. So that’s going to be 9 total routes. Inside the routes, we’re going to use nunjucks to render our views, so we’re going to need to have our data from the database in here. We’re going to do that by passing in our app variable, and our getDataFromMongoDB variable that we’ve already created.

var appRouter = function(app, queryMongo){
  app.get('/', function(req, res){
    res.render('home');
  })
  app.get('/males', function(req, res) {
    var query = { 'gender' : 'Male' }
    queryMongo(query, function(items) {
      res.render('results', { 'items': items } );
    });
  });
  app.get('/females', function(req, res) {
    var query = { 'gender' : 'Female' }
    queryMongo(query, function(items) {
      res.render('results', { 'items' : items } );
    });
  });
  app.get('/firstname/:name', function(req, res) {
    var query = { 'first_name' : req.params.name }
    queryMongo(query, function(items) {
      res.render('results', { 'items' : items } );
    });
  });
  app.get('/firstname', function(req, res) {
    res.render('search', { 'title' : 'firstname' });
  });
  app.get('/lastname/:name', function(req, res) {
    var query = { 'last_name' : req.params.name }
    queryMongo(query, function(items) {
      res.render('results', { 'items' : items } );
    });
  });
  app.get('/lastname', function(req, res) {
    res.render('search', { 'title' : 'lastname' });
  });
  app.get('/id/:id', function(req, res) {
    var query = { "id" : parseInt(req.params.id) }
    queryMongo(query, function(items) {
      res.render('results', { 'items' : items } );
    });
  });
  app.get('/id', function(req, res) {
    res.render('search', { 'title' : 'id' });
  });
}

So, first we declare appRouter, and we pass it App and queryMongo, which is our getDataFromMongoDB variable from app.js (we’ll show you how to pass that in shortly.)

We then use app.get() to declare a URL, and also set up a callback function that passes a request and result variable. We take the request variable and build our query, then pass that to queryMongo, which will trigger our database operation. We also define a callback function that accepts a single parameter named “items”. This is the array result that we got previously in our MongoDB call. It was passed back to this function by using callback(docs). Inside this function, we are going to use res.render() to declare which template we’re going to render, and then we’re going to pass it a JSON object called “Items”. We use the items array inside the JSON object so that we have them available on our template, which you can see below.

{% block header %}
<h1>Employee List</h1>
<div><p>Welcome to our employee list. Here you will find all of the infomation you need on the employees in our system. Below you will see several different ways to access employee data. Please take a moment to familiarize yourself with the options before continuing. Thank you.</p></div>
<div>
  <ul>
    <li><a href='/males/'>Male</a> - A list of all male employees</li>
    <li><a href='/females/'>Female</a> - A list of all female employees</li>
    <li><a href='/firstname/'>First Name</a> - Search for employees by first name</li>
    <li><a href='/lastname/'>Last Name</a> - Search for employees by last name</li>
    <li><a href='/id/'>ID</a> - search for employees by ID number</li>
  </ul>
</div>
{% endblock %}

<section class="results">
  {% block results %}
  {% endblock %}
</section>

This is a bit more advanced than the last tutorial I did involving nunjucks. This is going to involve templating and blocks. In the main template, (home.html) we have our basic descriptors of everything that’s going on. That’s the “header” block. (Blocks are declared with a {% followed by “block” and then the name of the block and %}) All of the content in block header would be displayed whenever the page is rendered.

Now, we have a second block called “results”. That is a sub template that extends our default template. Any time we call our results template, (results.html) – it will render home.html and plug in our results template into the results block.

{% extends "views/home.html" %}
{% block results %}

{% for item in items %}
<div style="float:left;width:350px;height:120px;">
<ul>
<li>First Name: <span>{{item.first_name}}</span></li>
<li>Last Name: <span>{{item.last_name}}</span></li>
<li>E-Mail: <span>{{item.email}}</span></li>
<li>Gender: <span>{{item.gender}}</span></li>
<li>IP Address: <span>{{item.ip_address}}</span></li>
</ul>
</div>
{% else %}
<div>Nobody!</div>
{% endfor %}
{% endblock %}

By calling the block in “results.html” results, we set it to appear in the “results” block on home.html. We ensure that it extends home.html with the first line – A note: even if this is in the same location as your home.html, you need to set your URL to work from the root, or it won’t display properly. You may need to play around with the URL a bit; it’s a tad inconsistent.)

We also use a for loop in this. Our items object is passed through, and as I said in the last tutorial, you can call json objects by wrapping them in {{}} brackets. So, if we passed an object like this through:

{ 'title' : 'President' }

and wrote something like this:

<p>Hello. I am the {{title}} of this company.</p>

You would get:

Hello. I am the President of this company.

Using the for command in nunjucks, we can iterate through each object in items. That’s why we needed to return the MongoDB data as an array. for item in items <– This line will cause our project to go through each portion of the array. We can then use dot notation, (item.first_name, for example) to display all of the data on the screen individually. This allows us to use a small section of code to display everything we need.

You’ll notice that firstname, lastname, and id all have two calls; one of these is to act as the default in case a parameter is not passed in. We want to show a search page so that people can search for whatever criteria they’ve decided to search for. So, we throw together a quick search page, with a little javascript in it to redirect the window, and we’ve got a working website.

<body>
  <script>
  function windowRedirect()
  {
    window.location = '/'+ document.getElementById("type").innerHTML + '/' + document.getElementById("search").value;
  }
  </script>
  <div>
    <h1>Search by <span id='type'>{{ title }}</span></h1>
  </div>
  <input type="text" name="id" id="search" />
  <button onclick="windowRedirect()">Search</button>
</body>

Almost. In the routes.js page, we need to add one final piece before that page is done. It tells our main app.js that it can be used in a ‘requires()’ call.

module.exports = appRouter;

Once you’ve put that in your route.js file, you can go back to the app.js file and put in the final pieces of code. The first line is going to pull in our routes, and pass app and getDataFromMongoDB to our routes.js file.

var routes = require("./routes/routes.js")(app, getDataFromMongoDB);

And, of course, our server won’t do anything unless we tell it to listen.

var server = app.listen('3000', 'localhost', function() {
    console.log("Listening on port %s...", server.address().port);
});

Follow these steps, and you’ll have yourself a working website that pulls data dynamically from a MongoDB server.

Hope you enjoyed this tutorial. Let me know if you have any questions, or if you have anything you’d like me to cover!

spacer

One comment on “Employee Website – a NodeJS example

  1. Pingback: NodeJS tutorial – MongoDB driven Employee Database – Entwined Studios

Leave a reply