Contents


Getting started with IBM Cloud Node.js applications, Part 2

Create a front-desk visitor log with Node.js

Developing the client-side code

Comments

Content series:

This content is part # of # in the series: Getting started with IBM Cloud Node.js applications, Part 2

Stay tuned for additional content in this series.

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

Run the appGet the code

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:

  1. 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).
  2. Request for a visitor to be logged in.
  3. Request for a visitor to be logged out.
Figure 1. The application's process
app process
app 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 greyDefault
BluePrimary
GreenSuccess
Light blueInformation
OrangeWarning
RedDanger

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.


Downloadable resources


Related topics


Comments

Sign in or register to add and subscribe to comments.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Cloud computing, Security
ArticleID=1007182
ArticleTitle=Getting started with IBM Cloud Node.js applications, Part 2: Create a front-desk visitor log with Node.js
publish-date=04092018