How to create a MEAN+ stack application (MongoDB, Express, Angular, Node + Bootstrap, Jade)

Steps

      Create a Node.js with Express.js App
      Create a Git repository and clone to localhost
      Add Angular.js
      Add Bootstrap
      Add MongoDb
      Add Jade
      Add an API for GET /people
      Connect to MongoDb
      Create a sortable table with Angular

Create a Node.js with Express.js App

I will be using Bluemix to host, build and deploy my Node.js application and the MongoDb service. I am developing on my localhost for unit testing before I upload my application to Bluemix, but you can also use the online editor and online Git instead.

1. If you do not have a Bluemix account yet, sign up for a free account at http://ibm.biz/bluemixnyc,
2. Once you have an account, go to your Bluemix console at https://console.ng.bluemix.net/,
3. Go to your Dashboard,
4. In the ‘Cloud Foundry Apps’ box, click ‘CREATE APP’,

5. Choose ‘WEB’


6. Select ‘SDK for Node.js’ and press ‘CONTINUE’,

7. Enter an application name, e.g. ‘<alias>-mean1’ (remkohdev-mean1),
8. Click ‘FINISH’.

Cloud Foundry creates your Node.js application and stages the application in the ‘SDK for Node.js’ runtime using a Node.js buildpack. Cloud Foundry is an open source container technology similar to Docker and Bluemix uses Cloud Foundry to create, deploy and manage your application. For more information, go to https://www.cloudfoundry.org/.

Create a Git repository and clone to localhost

1. If you are not on the application detail page, go to the Bluemix console, to the Dashboard, and click on the <app name> box, which will take you to the application detail page,

2. Click on the Overview link,
3. In the top right corner of the Overview page, click ‘ADD GIT’,

4. Check the ‘Populate the repository with the starter application package and enable Delivery Pipeline (Build & Deploy)’ checkbox.

5. Press ‘CONTINUE’,
6. Press ‘CLOSE’,
7. You now should see a link to your remote GIT repository and an ‘EDIT CODE’ button, in the top right corner instead of the ‘ADD GIT’ link.

8. Click on the git link then on the ‘EDIT CODE’ button, or click the ‘EDIT CODE’ button directly to go to the git repository,

You don’t have to clone the Git repository to your localhost to upload changes, alternatively you can use the online editor and commit changes online. If you use the online tool, you can skip the steps below.

1. In the Git repository on hub.jazz.net, in the ‘EDIt CODE’ page, find the ‘Git Url’ of our project.
2. Copy the Git Url of your project
3. In your favorite Git client clone the remote repository to your local development directory, authenticate with <alias> and password,
From command line:
$git clone https://<alias>:<IBM_ID_password>@hub.jazz.net/<alias>/<project_name>

To upload changes in your local repository to the remote Bluemix repository, I use the Cloud Foundry tool. To use the Cloud Foundry command line tool, Git or Eclipse, you can find instructions in your application’s ‘Start Coding’ page. You can download and install the Cloud Foundry executable from the binaries section from https://github.com/cloudfoundry/cli/releases.

When you have cloned the repository to your local directory and installed the Cloud Foundry tool, cd into your project directory, and using the Cloud Foundry commandline ‘cf’, connect to Bluemix, login to Bluemix, and push changes to your code with the ‘cf push <app_name>’ command.

cf api https://api.ng.bluemix.net
cf login -u <alias> -o <organization> -s <space>
cf push <app_name>

Note
If you run the Node.js application on localhost, make the following changes to the ‘app.js’, removing the Cloud Foundry specific method to retrieve the environment variables for the host and port.

/*eslint-env node*/
var express = require('express');
//note: on localhost cfenv will cause an error, we'll use the process.env method instead
//var cfenv = require('cfenv');
	
var app = express();
app.use(express.static(__dirname + '/public'));
	
//var appEnv = cfenv.getAppEnv();
var host = (process.env.VCAP_APP_HOST || 'localhost');
var port = (process.env.VCAP_APP_PORT || 3001);
	
var server = app.listen(port, host, function() {
  console.log(\"server starting on \" + host + \":\" + port);
});

Now push the changes to Bluemix, which will also restage your application.

cf push

You can use the following command to get detailed logging,

cf logs <app_name> --recent

Add Angular.js

Now you need to add Angular support.
1. Go to https://angularjs.org/ and download the latest version of the ‘angular.min.js’ file.
2. in your Node.js project add a ‘./public/js/lib’ folder for JavaScript libraries like Angular.
3. Copy paste the ‘angular.min.js’ file into the ‘./public/js/lib’ folder.
4. In your ‘./public/index.html’ file, import the angular library, add the following line right before the closing body-tag.

<script type=\"text/javascript\" src=\"/js/lib/angular.min.js\"></script>

5. Next, to test our Angular configuration, change the body-tag content and add a dynamic input handler

<body ng-app="myapp">
    <h1>Hello MEAN+</h1>
    Test Angular: 2+3= {{ 2+3 }}<br>
    Type your name <input ng-model=\"name\"> Hello {{ name }}

– The ‘ng-app’ in the body-tag bootstraps Angular.js and sets the body-tag as the root. Alternatively, you can add it to the html-tag.
– The {{ 2+3 }} is an Angular expression that will get evaluated at runtime and should resolve to ‘5’ if Angular works properly.
– The ‘ng-model’ binds form elements to a property called $scope, the {{ name }} template outputs the value of the form element.

6. We still need to instantiate the Angular application. This is done by adding an ‘angular.module’ definition. Most applications have a ‘main’ method, but Angular has a module that instantiates the application. We have named our application ‘myapp’ in the ‘ng-app’ property. First create a new JavaScript file ‘meanplus.js’ in the ‘/public/js’ directory and import the file with a script-tag in your ‘index.html’ file.

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

7. In the ‘meanplus.js’ file add the following code.

var myapp = angular.module('myapp', []);


If all works as we planned, you should now be able to run your Node.js application, type a value in the input element and immediately see the value outputted in the template tag.

Add Bootstrap

Now we have added Angular, you can add Bootstrap for a responsive design.
1. Go to http://getbootstrap.com/ and download the latest version of the bootstrap distribution,
2. Unzip the bootstrap distribution,
3. Copy-paste the ‘bootstrap.min.js’ file into the ‘./public/js/lib’ folder.
4. Add the following line right before the body-tag in your ‘index.html’ file.

<script type=\"text/javascript\" src=\"/js/lib/bootstrap.min.js\"></script>

5. Copy paste the ‘bootstrap.min.css’ file into the ‘./public/stylesheets/lib’ folder, create the folder if it doesn’t exist.
6. Add the following line to your head-tag in the ‘index.html’ file,

<link rel=\"stylesheet\" href=\"stylesheets/lib/bootstrap.min.css\">

7. Add a Bootstrap grid system to the body of the ‘index.html’ file,

<body ng-app="myapp">
<div class="container">
  <div class="row">
    <div class="col-md-12">
    <h1>Hello MEAN+</h1>
    </div>
  </div>
  <div class="row">
    <div class="col-md-3">
      Test Angular: 2+3= {{ 2+3 }}<br>
    </div>
    <div class="col-md-6">
      Type your name <input ng-model=\"name\"> Hello {{ name }}
    </div>
    <div class="col-md-3">
    </div>
  </div>
</div>

The above should respond as below.

Add MongoDb

Almost done! You have an EAN+ application now, but the M from MongoDb is still missing, so let’s add it. In Bluemix, go to your application Overview page, via Dashboard, click the application box, and click Overview. We need to add a MongoDb service, or if you already created a MongoDb service, you need to bind the existing MongoDb service to your application. This will create an instance of MongoDb, while the binding creates the MongoDb service credentials by which the service for this application is uniquely identified.

1. In the application Overview page, click the ‘ADD A SERVICE OR API’,

2. In the Catalog, you can choose one of the following MongoDb instances:
I. MongoDB by Compose,
II. MongoLab, or
III. In the Bluemix Labs Catalog, MongoDb.

I will choose the MongoLab instance. Select the MongoLab in the Catalog, and in the service configuration window, use the defaults. You can change the service instance name, if you want. Keep the ‘Sandbox’ plan for the service. Click ‘USE’. This will create an instance of the service and add application specific service credentials and configuration details.

3. You will be asked to restage the application, click ‘RESTAGE’.

4. Go back the application Overview page, and you will see the MongoDb service added to your application. In the MongoDb service box, click the ‘Show Credentials’ link to show your service credentials and configuration details.

{
  \"mongolab\": [
    {
      \"name\": \"MongoLab-j6\",
      \"label\": \"mongolab\",
      \"plan\": \"sandbox\",
      \"credentials\": {
        \"uri\": \"mongodb://<username>:<password>@<instance_name>.mongolab.com:<port>/<username_prefix>\"
      }
    }
  ]
}

You have a MEAN application!

Add Jade

Another common addition in the MEAN stack is to use a Node Template Engine, two common ones are Jade and EJS (Embedded JavaScript). Jade greatly simplifies writing your pages, with a simple page syntax that eliminates the tags syntax, but also allows for layout templates and extensions and data binding.

To enable Jade in your Node.js application, add the following to your app.js file.

var jade = require('jade');
app.set('view engine', 'jade');

In the ‘package.json’ file, we must add support for the Jade module. Add the Jade module, and the MongoDB module (we need MongoDB for later) dependencies.

\"dependencies\": {
    \"express\": \"4.12.x\",
    \"cfenv\": \"1.0.x\",
    \"jade\":\"*\",
    \"mongodb\":\"*\"
}

We will place all pages and templates in a separate ‘views’ directory, and for the application to find the pages, you must add the following line to your app.js file.

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

Now, create a layout template and extend the layout in the index page.

In the new ‘views’ directory, create a new file named ‘layout.jade’ and add the following template. Compared to the index.html file from the starter application, I removed the header content, because I will create a header in the index.jade template instead.

doctype html
html(ng-app='myapp')
  head
    title #{title}
    meta(name='viewport', content='width=device-width, initial-scale=1')
    meta(charset='utf-8')
    meta(http-equiv=\"X-UA-Compatible\", content=\"IE=edge\")
    link(rel='stylesheet', href='/stylesheets/style.css')
    link(rel='stylesheet', href='/stylesheets/lib/bootstrap.min.css')
    script(type='text/javascript', src='/js/lib/angular.min.js')
    script(type='text/javascript', src='/js/lib/bootstrap.min.js')
    script(type='text/javascript', src='/js/meanplus.js')
  body
    header
    .container
      .main-content
        block content

The title tag is set by a #{title} object, we will set the value of the title in the method below. This is called interpolation. Both Jade and Angular can handle interpolation, but apart from this Jade example, I will use Angular as the main tool for interpolation.

Also add a new file ‘index.jade’ with the following content.

extend layout
block content
  div
    .row
      .col-md-12
        h1 Hello MEAN+
    .row
      .col-md-3
        test angular: 5+3={{ 5+3 }}
      .col-md-6
        | Type your name:
        input(ng-model=\"name\")
        | Hello {{ name }}
      .col-md-3

As you see by the use of the .row and .col classes, I am using the Bootstrap grid system. You also see that I extend the ‘layout’ template and implement the ‘block content’, which is defined in the ‘index.jade’ template. The simplicity and leanness of the ‘index.jade’ page clearly shows the benefit of using a template engine like Jade, plus it allows us to reuse all the includes and shared definitions.

To make sure that the Node application loads the new ‘index.jade’ page instead of the index.html in the ‘/public’ directory, add this route to the ‘app.js’ file.
We also set the value of the ‘title’ object in the title-tag, that we added to the ‘layout.jade’ file.

app.get('/', function (req, res){
    console.log(\"get /\");
    res.render('index', { title : 'Hello MEAN+ - Home' });
});

You can delete or rename the 'index.html' now.

Add an API for GET /people

Your MEAN+ application is now configured and ready to start coding! Your complete app.js file should look as follows:

/*eslint-env node*/
 
var express = require('express');
//var cfenv = require('cfenv');
var jade = require('jade');
 
var app = express();
app.use(express.static(__dirname + '/public'));
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
 
app.get('/', function (req, res){
 console.log(\"get /\");
 res.render('index', { title : 'MEAN+ Home' });
});
 
//var appEnv = cfenv.getAppEnv();
var host = (process.env.VCAP_APP_HOST || 'localhost');
var port = (process.env.VCAP_APP_PORT || 3001);
var server = app.listen(port, host, function() {
 console.log(\"server starting on \" + host + \":\" + port);
});

So let's add some functionality to our MEAN+ application now. First, let's add an API endpoint to 'GET /people' and create a 'people.jade' page in the 'views' directory to display the response.

In the 'app'js' file, create a list of people, and add a route for GET '/people'.

var people = {
    {
        'person_id' : 1,
        'first_name': 'john',
        'email': 'john@email.com'
    }, {
        'person_id' : 2,
        'first_name': 'peter',
        'email': 'peter@email.com'
    }, {
        'person_id' : 3,
        'first_name': 'max',
        'email': 'max@email.com'
    }
};
	
app.get('/people', function(req, res){
    console.log('get /people');
    res.render('people', { title : 'People', people : people });
});

In the '/people' request handler or router, we return the 'people' page, which we will create next, and pass the 'people' variable with a list of people in the response to the 'people' page.

In the 'views' directory now create a new file named 'people.jade'.

extend layout
block content
  div(ng-controller='peopleCtrl')
    .row
      .col-md-3
        | Person Id
      .col-md-3
        | First name
      .col-md-3
        | Last name
    .row(ng-repeat='person in people2')
      .col-md-3
        | {{person.person_id}}
      .col-md-3
        | {{person.first_name}}
      .col-md-3
        | {{person.email}}
  script.
    var people1 = !{JSON.stringify(people)}

In this code, you extend the 'layout' template again and implement the 'content' block similar to what we did in the 'index' template. You also are using the same bootstrap grid as before, but in this case, the first row is a table header, the second row is a repeating row, repeating itself for each 'person in people2'. The first column will display the 'person_id', the second column the 'first_name' of the 'person', and the third column will display the 'email' of the 'person'. In our example we pass 3 people to the page, which should result in 3 rows.

The 'people' variable is a list that was passed by the '/people' request handler in the 'app.js'. At the bottom of the page, is a line of JavaScript code which takes the 'people' response object returned from 'app.js', stringifies it to properly render it, and sets a new global JavaScript variable 'people1' to the correctly rendered 'people' response object. Now, our JavaScript code in our page can access the properly formed response object of people.

We also defined the 'ng-controller', which is a key component in Angular's Model-View-Controller (MVC) architecture. One last thing to do, is to implement this controller in the 'meanplus.js' file. We declare the controller on the 'myapp' instance and connect it by name, in this case the name of the controller is 'peopleCtrl'. In the implementation of the function, we then set the variable 'people2' on the Angular $scope property of the controller 'peopleCtrl' to the properly formed 'people1' variable from the script at the bottom of our page. This $scope.people2 property is then read by the Angular 'ng-repeat' function to create the rows of people.

var myapp = angular.module('myapp', []);
	
myapp.controller('peopleCtrl', function($scope) {
	$scope.people2 = people1;
});

If you run the application from localhost, you can start your Node application, and in your browser paste the request in the address bar as follows: http://localhost:3001/people. Or if you run and deploy in Bluemix online, add /people to your request, which should now return a list of people in the result page.

Connect to MongoDb

Now let's connect to MongoDb and retrieve the list of people from the MongoDb instead from the hard-coded variable.

First, use a MongoDb client to connect to MongoDb and create a 'people' collection. Then insert the json documents for each of the persons in people into the people collection, so you have 3 documents in the 'people' collection.

I used the MongoLab Dashboard via Bluemix. You can access the MongoLab Dashboard, in the application detail page in Bluemix.

Go to Collections.

And add the three sample persons.

Another MongoDb client you can use is Robomongo.

In Bluemix, in the application Overview page, the MongoLab service has credentials, which you need to create a MongoDb connection. Create a services variable in 'app.js' to access the credentials when you run the application on localhost.

var services = {
  'mongolab': [
   {
     'name': 'MongoLab-j6',
     'label': 'mongolab',
     'plan': 'sandbox',
     'credentials': {
       'uri': 'mongodb://<username>:<password>@<database_domain>.mongolab.com:<port>/<database_name>'
     }
   }
 ]
};

When you run the application on Bluemix, you can retrieve the services credentials via this method.

var services = JSON.parse(process.env.VCAP_SERVICES);

At the top of your 'app.js' file, add the following require statement to support the 'mongodb' module. We already added this module in our 'package.json' file earlier.

var mongoDb = require('mongodb');

Replace the implementation of 'GET /people' by the following code in 'app.js'.

app.get('/people', function(req, res){
    console.log(\"get /people\");
    var mongoUri = services.mongolab[0].credentials.uri;
    // create a mongodb client
    var mongoClient = mongoDb.MongoClient;
    // connect to mongodb
    mongoClient.connect(mongoUri, function(err1, db) {
        if(err1) throw err1;
	console.log(\"MongoDb.connect\");
	// access the 'people' collection
	db.collection('people', function(err2, collection) {
	    if(err2) throw err2;
            console.log(\"MongoDb.collection\");
	    // find all people in the collection using an empty query
            collection.find({}).toArray(function(err3, docs) {
                if(err3) throw err3;
	        console.log(\"MongoDb.collection.find\");
	        // close the connection
	        db.close();
                // convert the array to string
	        var str = JSON.stringify(docs);
                // convert the string to json
	        var people = JSON.parse(str);
                // render the 'people' page
	        res.render('people', { title : 'People', people : people });
	    });
	});
    });
});

Create a sortable table with Angular

Here's a little extra in Angular.js, how to convert the People table into a sortable table.

In the peopleCtrl controller in 'meanplus.js' add a 'sortType' variable to the Angular $scope variable, this variable will store the current column to sort by.

var myapp = angular.module('myapp', []);
	
myapp.controller('peopleCtrl', function($scope) {
    $scope.sortType = 'first_name';
    $scope.people2 = people1;
});

Open the 'people.jade' template, and add a row under the h1-tag to display the '$scope.sortType' variable, the current sort column.

.row
  .col-md-12
    p Sort Type: {{ sortType }}

Add links to sort the table to each of the table headers.

.col-md-3
  a(href="#" ng-click="sortType='person_id'") Person_id

Add a 'orderBy:sortType' property to the repeatable rows.

.row(ng-repeat="person in people2 | orderBy:sortType")

So your total 'people.jade' template looks as follows.

extend layout
block content
  div(ng-controller="peopleCtrl")
    .row
      .col-md-12
        h1 Hello MEAN+ | People
    .row
      .col-md-12
        p Sort Type: {{ sortType }}
    .row
      .col-md-3
        a(href="#" ng-click="sortType='person_id'") Person_id
      .col-md-3
        a(href="#" ng-click="sortType='first_name'") First name
      .col-md-3
        a(href="#" ng-click="sortType='email'") Email
    .row(ng-repeat="person in people2 | orderBy:sortType")
      .col-md-3
        | {{person.person_id}}
      .col-md-3
        | {{person.first_name}}
      .col-md-3
        | {{person.email}}
  script.
    var people1 = !{JSON.stringify(people)}

Your table should now be sortable, by clicking the table headers.
By first name:

By person id:

View the source code at https://hub.jazz.net/project/remkohdev1/remkohdev-mean1/overview.
View the running application at http://remkohdev-mean1.mybluemix.net/.

Bluemix button
Note: When you click the button, the deploy phase in the 'Deploy to Bluemix' will fail if the MongoDb service with the exact service name as in the manifest.yml file does not exist in your space. You must create the MongoDb service with name 'MongoLab-j6' before you press the 'Deploy to Bluemix' button, or add a line as below to your manifest and edit the name to your own MongoDb service name and redeploy.

- services
  - yourmongodb-s1

Sign up for Bluemix here.
Coming up: 'Creating a Node.js application with Strongloop.io' and 'Creating a Node.js REST API server with Swagger.io'.

Print Friendly, PDF & Email

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.