Getting started with IBM Cloud Node.js applications, Part 2
Create a front-desk visitor log with Node.js
Developing the client-side code
Content series:
This content is part # of # in the series: Getting started with IBM Cloud Node.js applications, Part 2
This content is part of the series:Getting started with IBM Cloud Node.js applications, Part 2
Stay tuned for additional content in this series.
In the first article in this series, you learned how to build a Node.js application on the IBM Cloud. However, that application was not very responsive or visually appealing. In this article you learn how to use the Bootstrap theme for your application to look better, and how to use the AngularJS library to make it more responsive.
What you'll need to build your application
- A free IBM Cloud account.
- Basic knowledge of HTML.
- Basic knowledge of JavaScript.
- The application and information from the first article in this two-part series.
Update the application
To run the application with the new user interface, go back to the development environment you created in the first article and do these steps:
- Modify app.js to uncomment the call to serve static files out of the
public directory:
app.use(express.static(__dirname + '/public'));
- Delete public/index.html, and any directories under /public. To delete files in the online development environment, right-click them and select Delete.
- Create a new file, public/ui.html, and put in it the content from this link.
- Restart the application.
- Browse to
<your URL>/ui.html
to see the new user interface. Alternatively, you can see my copy here.
How does it work?
The process the application uses is shown below in Figure 1. When the user browses to the application, the application on the IBM Cloud responds with the ui.html, an HTML file that has a lot of JavaScript code in it.
The client-side code in ui.html communicates with the server-side code in app.js in three ways:
- Ask the server for the value of the visitors data structure. The server responds with this information, which the client can then manipulate and display to the user. This is done at startup, after requesting a change, and every few minutes (in case a different user changes the information).
- Request for a visitor to be logged in.
- Request for a visitor to be logged out.
Figure 1. The application's process

The client-side application uses reactive programming to display the visitors.
Line by line explanation
This is the detailed explanation. You can follow with the source code here.
The syntax for a comment in HTML is
<!-- comment -->
.
For example:
<!-- jQuery -->
Knowledge of jQuery library is a
prerequisite for working with AngularJS. It has a number of useful
functions, such as >script src...>
.
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
The <script src...>
syntax imports JavaScript from
a URL. In this case, it imports the minimized (optimized for bandwidth,
not readability) version of the jQuery library. You can find the readable version of jQuery here, but it is not necessary to read
or understand it to use AngularJS. It is just necessary to import it so
its features will be available for AngularJS to use.
The Bootstrap theme is what defines the application's look and feel. To learn how to use Bootstrap, you can view the sample and read the source code to see how different items are created.
<!-- Bootstrap --> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap-theme.min.css"> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
AngularJS is the library that we use for reactive programming:
<!-- Angular --> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.js"></script>
Angular has additional modules. One of them, sanitize, is used to display HTML from potentially dangerous sources. We will need it later.
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-sanitize.js"></script> <script>
Here's the command to create an Angular application:
var myApp = angular.module("myApp", ['ngSanitize']);
One of the most important concepts in Angular is the
scope. The scope variable contains most of the controller
functions, and the variables that are displayed to the user. It is not
necessary to access the scope from outside a controller, but I find
var scope;
useful for debugging purposes.
This call creates the controller and runs the provided function with the
parameter $scope
, which is the scope for that controller.
myApp.controller("myCtrl", function($scope) { // Make the scope available outside the controller, // which is very useful for debugging scope = $scope;
This variable is going to be the same as visitors
on the
server side. For now, just put a dummy value in it:
$scope.visitors = {a: "b"};
The visitor is logged in and the if that
value is typed
instead of being selected. This variable is the model for a text field,
into which the user can type the name of a visitor, which means that the
text field and the variable always have the same value. If one changes,
AngularJS changes the other. This variable is the model for a text field,
(which you will see when we get there):
$scope.loginUser = "";
The following function reads visitors
from the server side and
updates it in the client.
$scope.refreshVisitors = () => {
The variable $ is a data structure that contains the jQuery functions. One
of them, $.ajax
, can read data asynchronously. We call it
here to read the visitors' data structure. Because app.js does not provide
a content type, we specify the data type here:
$.ajax({ url: "/test/visitors", async: true, dataType: "json",
This is the callback function. Because the data type is JSON, it receives a
parsed JavaScript object as the parameter. It sets the scope variable to
the new value, and then calls $scope.$apply
. Whenever a
variable in a reactive programming framework such as AngularJS changes, it is
necessary to figure out what other items based on that variable need to
change. Normally AngularJS can do this for us, but in the case of changes
coming asynchronously from a callback function it can't so we need to call
$scope.$apply
manually.
success: (obj) => { $scope.visitors = obj; $scope.$apply(); } }); };
Refresh the visitors now (when the controller is created) and use setInterval
to do it every minute.
$scope.refreshVisitors(); // Refresh visitors every minute (in case somebody // else changes it) setInterval($scope.refreshVisitors, 60*1000);
This function makes a lot more sense where it is called, as it returns the current time in milliseconds since the beginning of the epoch. The reason that this function is required is explained later in this article where it is used.
// Date.now() doesn't function correctly inside {{}}, so // use this $scope.now = () => Date.now().valueOf();
This function logs a visitor out. It does this by calling the same URL the web page in the first article in the series called. The function to log a visitor in is almost identical (the only difference is the URL), so it is not shown here.
// Log a visitor out (ask the server to do it, then refresh // the visitors data structure) $scope.logout = name => { $.ajax({
The visitor name is likely to contain characters that are invalid in an HTML query
string. The encodeURI
function escapes those
characters so that the query will be valid and the app.js code finds the
correct visitor name.
url: `logout?user=${encodeURI(name)}`, async: true, success: () => $scope.refreshVisitors() }); };
A lot of the functions that we developed in app.js in the previous article are also useful here. This is one of the advantages of using Node.js for web applications, since you can often reuse the same code in the server and the client as they are both from JavaScript.
// Functions from app.js $scope.visitorNames = … $scope.currentVisitorNames = … $scope.nonCurrentVisitorNames = … $scope.tdiffToString = …
This function is a bit different from the app.js version. In the app.js
version, it is enough to use Date(value)
to get the string
representation of the time and date. However, because it reuses the Date
object and the way AngularJS organizes the evaluation, if we use the same
function here all the displayed dates will be the same. To prevent this,
we create a new Date object every time.
$scope.histEntryToRow = (entry) => { return `<tr> <td>${new Date(entry.arrived)}</td> <td>${new Date(entry.left)}</td> <td>${$scope.tdiffToString(entry.left – entry.arrived)}</td> </tr>`; };
This function is also different because the client-side code uses the Bootstrap theme. Instead of specifying directly how the table should look, it uses Bootstrap classes.
// Given a history, create a table with it $scope.histToTable = (history) => { … return `<details> <table class="table table-condensed table-border"> <tr> … `; }; }); </script> </head>
The body tag includes two AngularJS attributes, one for the application and the other for the controller. Complex Angular applications can have multiple controllers.
<body ng-app="myApp" ng-controller="myCtrl">
An HTML
div
element is a rectangular container on the page. A
Bootstrap panel is implemented as a div
with the
panel
class. The second class, panel-primary
,
specifies the color. Bootstrap uses background colors to communicate
information (not just for panels, for all the theme's elements):
Sample data table
Light grey | Default |
Blue | Primary |
Green | Success |
Light blue | Information |
Orange | Warning |
Red | Danger |
A panel typically has two div
elements inside it, a panel
heading and a panel body.
<div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title">Current visitors</h3> </div> <div class="panel-body"> <table class="table"> <tr> <th>Name</th> <th></th> <th>Time here</th> <th>History</th>
This is where AngularJS gets involved. The
ng-repeat
attribute specifies that the tag, in this
case a table row, is to be repeated for every value in a list. The list is
the list of current visitors, and the current value is put in the
name
variable.
<tr ng-repeat="name in currentVisitorNames()" > <td>
When AngularJS sees the syntax {{<expression>}}
, it
evaluates the expression and replaces the double brackets with the result.
In this case, it means that the name of the visitor will appear in a table
cell.
{{name}} </td> <td>
The normal on-click
attribute doesn't include AngularJS
evaluation. For example, to use AngularJS the scope function
logout
with the ng-repeat index name, we use the
ng-click
attribute.
<button class="btn btn-danger" ng-click="logout(name)"> Logout </button> </td> <td>
If we try to run Date.now()
directly inside double brackets
({{…}}
), it fails because it needs to create a new object.
But if it is inside a function AngularJS runs the function when it is able
to do so and then it works as expected.
The last {{ tdiffToString(now()-visitors[name].arrived)}} </td> <td>
Here we see several new things. The first is the
details
tag. This tag allows us to create information
that is only shown when the user clicks on it. This functionality is
perfect for history, which needs to be available but is usually not
relevant. Within this tag, the
ng-if
attribute asks AngularJS to decide if this tag
should appear or not. If the condition evaluates to false, the user does
not see the non-existent history table. Finally, because the table is
HTML, we can't just put it between brackets – in such a case it will be
interpreted as text, not HTML. Instead, we use the
ng-bind-html
attribute.
Note: This attribute is the reason we needed
ngSanitize
earlier. That library removes links and other
potentially dangerous content from HTML that might come from an untrusted
source. If you need to allow certain links that go through AngularJS
interpretation, there's a way to tell it to make an exception in that
case.
<details ng-if="visitors[name].history && visitors[name].history.length > 0" ng-bind-html="histToTable(visitors[name].history)"> </details> </td> </tr> </table> </div> </div> <!-- current visitors -->
The panel with the visitors that are not currently here uses the same techniques and definitions, so there's no need to explain them again.
The bottom panel for the log in is different, because it needs to have a text input to let the user specify a new visitor name.
<div class="panel panel-success"> <div class="panel-heading"> <h3 class="panel-title">Log In a Visitor</h3> </div> <div class="panel-body">
The ng-model
attribute ties an input element's value to
a scope variable. This is bidirectional. Whenever the user types in the
text field the value of loginUser
changes, and whenever the
value of loginUser
changes the new value appears in the text
field (as well as the button bellow it).
<input type="text" ng-model="loginUser"> <br /> <button class="btn btn-success" ng-click="login(loginUser)"> Log on {{loginUser}} </button> </div> </div> </body> </html>
Conclusion
After reading both articles from this series, you now have the knowledge to write simple web applications that respond to user input and store that data in a Cloudant database. This may not sound like a lot, but in combination with all the services available in the IBM Cloud, you can write very sophisticated applications to achieve a wide variety of goals.