One of my Raspberry Pi (minime) sensing the temperature and other weather information in Durham
One of my Raspberry Pi (minime) sensing the temperature and other weather information in Durham / Photo © Jean Georges Perrin

I have been looking for a front-end framework that is easy to learn, powerful, that can be good at SEO, and that can be really great at implementing rich and welcoming UX (User eXperience).

For a long time, I was using GWT (Google Web Toolkit) and we developed quite a few projects with it at GreenIvory, M Systems, and others. One of the issue of GWT apps was that you were entering an application: it means that it does not integrate well with its environment, does not deal well with bookmarks (I know there are ways) or CMS (Content Management Systems).

Here comes AngularJS. We gave it a try back in 2013 on some projects but our legacy GWT apps made it difficult to switch.

Scope and Goal

Basic display I wanted to achieve for my Raspberry Pi and sensor information
Basic display I wanted to achieve for my Raspberry Pi and sensor information

As I was starting a new project, I could pick anything I wanted and decided to give a chance to AngularJS. To get up to speed, I found a course taught by Girl Develop It (RDU Chapter) and specially our young and dynamic teacher Erin Brown.

There are definitely plenty of tutorials on AngularJS out there and my goal is not to build “yet another one”, but trying to describe a small project I am working on and understand the ins and outs of the front end part.

I have a Raspberry Pi, measuring the temperature and various other stuff. It sends the data to “the cloud” and it is made available back to a client.

JSON Data Fragment

The JSON data can be downloaded from the 4iot.io website at: http://api.4iot.io/device?t=b518b1cfd18b9da7fdaa11e08d603072a. A lint version of it can look like:

{
	"name": "pink-raspberry-on-the-go",
	"cpuCount": 4,
	"tsReceiver": "2016-05-25T14:51:34.370",
	"swapUsed": 0,
	"osArch": "ARM_7",
	"osKernel": "4.4.11-v7+",
	"cpuTemp": 45.1,
	"memAvail": 545,
	"UpTime": "2 00:27:42",
	"network": {
		"wlan0": {
			"ipv6": "fe80::251e:91a8:fb31:f697\/64",
			"mac": "b8:27:eb:1d:42:17"
		},
		"eth0": {
			"ipv6": "fe80::f04b:8e08:f116:38c5\/64",
			"ipv4": "192.168.0.12",
			"mac": "b8:27:eb:48:17:42"
		}
	},
	"cpuIdle": 99,
	"id": "b518b1cfd18b9da7fdaa11e08d603072a",
	"cpuUser": 0,
	"memUsed": 316,
	"ioWait": 0,
	"os": "Linux",
	"osVersion": "8.0",
	"pressure": 3947.8779296875,
	"osDist": "debian",
	"cpu": "ARMv7 Processor rev 4 (v7l)",
	"cpuSystem": 0,
	"swapAvail": 99,
	"formatVersion": 2,
	"humidity": 38.58238983154297,
	"tsClient": "2016-05-25T14:51:02.577483",
	"seq": 1464202262578,
	"temp": 26.948138427734378,
	"clientIp": "173.95.180.43",
	"cpuLoad": 82.1,
	"storage": {
		"\/boot": {
			"diskDev": "\/dev\/mmcblk0p1",
			"diskAvail": 38,
			"diskUsed": 21
		},
		"\/": {
			"diskDev": "\/dev\/root",
			"diskAvail": 52278,
			"diskUsed": 5063
		}
	}
}

Despite that I am impressed by the precision of the temperature (26.948138427734378 ºC), you can see that it brings quite a bit of data on the Pi itself.

So how can I consume my data and do something with it using AngularJS?

The Core of the UI, the HTML page

So let’s start by the HTML page: device.html.

The first part of the page is really the classic stuff. As a side note, the idea developed in this framework is close to what we did in 2002, when we built ThinStructure at Awoma: leave the UI as neat as possible.

<!DOCTYPE html>
<html>
<head>

<title>Your device</title>

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

<script type="text/javascript" src="js/jquery-2.2.3.min.js"></script>

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" integrity="sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r" crossorigin="anonymous" />

<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>
<link href="css/4iot.min.css" rel="stylesheet" />
<script src="https://use.fontawesome.com/08f48c0c80.js"></script>
</head>

On line 7, you can see a call to integrate the AngularJS script. We kept this one locally, in the js directory on the server. This is really the exact same file as the one you can download from the AngularJS server.

On line 8, we call device.js, which contains our code, you can see more about it a little later.

On line 10, we integrate jquery, which is going to be used by some Bootstrap widgets, which we integrate, directly from CDN on line 12, 17, and 22.

On line 26, we use a little bit of our own CSS, but nothing to worry about.

Finally, on line 27, we use Fontawesome, also directly from CDN.

<body ng-app="deviceApp">

We jump into AngularJS. We add to the body element the ng-app attribute. In AngularJS’ terminology, this is called a directive. We give it a special name: deviceApp.

 ➡ Tip:
AngularJS directives are prefix by ng-. It is highly recommended that you do not use the same prefix.

If we give a quick look at device.js (we will study it in detail later), we can see that there is a link here, both use deviceApp.

(function() {
	angular
			.module('deviceApp', [])

But let’s continue our journey through the HTML file.


<h2>Your device</h2>


<section ng-controller="DeviceController as deviceCtrl">

On line 33, we can see something rather unusual: we have a section element along with a ng-controller directive.

We used the section element as we just need to create a context where we could use a controller. As section does not exist in HTML, it is a perfect placeholder. We are instructing the JavaScript engine that we want to use the DeviceController controller and we are going to name the instance of it: deviceCtrl.

 ➡ Tip:
It is best practice to define the controller as PascalCase and the instance in camelCase. Like in Java :). It is also a good practice to suffix your instance by Ctrl.

Once more, a quick look at device.js, will show:

			.controller(
					'DeviceController',

In the JavaScript file, you can see that we use the controller “class” name, not the instance.

 ➡ Tip:
As you can see, you are going to create a lot of links between your HTML page and your JavaScript code, be very careful about the syntax of those links.

	<h2>Your device</h2>
	<section ng-controller="DeviceController as deviceCtrl">
		<p>
			Device: <input type="text" value="{{deviceCtrl.device.name}}" />
		</p>
		<p>Id: {{deviceCtrl.device.id}}</p>
		<p>Thing's public IP: {{deviceCtrl.device.clientIp}}</p>

		<h3><i class="fa fa-bolt"></i>&nbsp;Weather Information</h3>
		<p>Temperature: {{deviceCtrl.device.temp | number:1 }}&deg;C</p>
		<p>Humidity: {{deviceCtrl.device.humidity | number:1 }}%</p>

		<h3><i class="fa fa-bolt"></i>&nbsp;CPU</h3>
		<p>CPU: {{deviceCtrl.device.cpu}}</p>
		<p>CPU Temperature:</p>

The following part is pretty simple: it’s about displaying the information we gather. The syntax is pretty simple: the content between double curly braces ({{}}) is transferred to AngularJS. So, on line 33, you can see that the device’s name will be displayed in an input box. The syntax deviceCtrl.device.name, makes a little more sense when you look as the JavaScript code below (line 33), as well as the JSON fragment (line 2):

									} else {
										self.device = data;
										$scope.$digest();
									}

Line 39 uses filter with the syntax {{deviceCtrl.device.temp | number:1 }}, the result is piped to a filter called number, which simply cuts all characters after the first decimal figure. Our crazy 26.948138427734378 ºC simply becomes 26.9 ºC.

The last part of the HTML page uses our own directives:

	<div iot-bar progress="deviceCtrl.device.cpuTemp" unit="'&deg;C'"></div>

	<p>CPU Usage:</p>
        <div iot-bar progress="deviceCtrl.device.cpuLoad" unit="'%'"></div>

Our directive is turning a value into a progress / gauge bar. What we need is a progress value and a unit. Through the use of AngularJS, it turns a template into HTML included in the page, allowing visual componentization. You can see the directive to be a bit like a function, in our case, this would be like calling iotBar(deviceCtrl.device.cpuTemp, '°C').

Look careful at the named parameters (the attributes):

  • progress="deviceCtrl.device.cpuTemp" passes the value of deviceCtrl.device.cpuTemp, not the string "deviceCtrl.device.cpuLoad".
  • unit="'°C'" passes the string '°C'.

The template is fairly dull (and uses Bootstrap):


<div class="progress">

<div class="progress-bar" role="progressbar" aria-valuenow="{{ progress }}" aria-valuemin="0" aria-valuemax="100" style="width: {{ progress }}%">{{ progress }}{{ unit }}</div>

</div>

And we close all of it…

	</section>

</body>

</html>

Walkthrough our JavaScript

Now, let’s have a look at the JavaScript, which is really the glue between the HTML (both the template and the page), the server, and AngularJS.

The first part of device.js is really a utility method to get a JSON document from a server, it is a copy/paste of what one can find anywhere.

var getJSON = function(url, callback) {
	var xhr = new XMLHttpRequest();
	xhr.open("get", url, true);
	xhr.responseType = "json";
	xhr.onload = function() {
		var status = xhr.status;
		if (status == 200) {
			callback(null, xhr.response);
		} else {
			callback(status);
		}
	};
	xhr.send();
};

It opens a HTTP GET connection to the URL you give it (line 7), expects JSON (line 8), and deals with the plumbing.

(function() {
	angular
			.module('deviceApp', [])
			.controller(
					'DeviceController',
					function($scope) {
						var self = this;
						getJSON(
								"http://api.4iot.io/device?t=b518b1cfd18b9da7fdaa11e08d603072a",
								function(err, data) {
									if (err != null) {
										alert("Something went wrong: " + err);
									} else {
										self.device = data;
										$scope.$digest();
									}
								});
					})
			.directive(
					'iotBar',
					function() {
						return {
							scope : {
								progress : "=",
								unit : "="
							},
							templateUrl : "tpl/progressBar.html"
						}
					});
})()

In this fragment, we can clearly see 3 blocks, the module, the controller, and the directive.

Module

The module is defined by angular.module('deviceApp', []). In our case we do not have anything the second parameter of the method call as we do not use any additional libraries (or modules).

 ➡ Tip:
Remember that deviceApp is used in both the JavaScript and HTML.

Controller

The controller is defined by angular.controller(). The controller is really the meat of the application.

After you name it (DeviceController), you pass it a function with the special $scope variable. $scope is an object that refers to the application model. Basically: the application itself.

If everything goes as planned, on line 33, you assigne the JSON data from the server to the variable device. At this point, you should completely understand the notation deviceCtrl.device.clientIp:

  • deviceCtrl is the instance of the controller.
  • device is the variable we just assigned data to.
  • clientIp is a field in the JSON document.

On line 34, $scope.$digest(); forces AngularJS to refresh its data after we received the values from the server.

 ➡ Tip:
Remember that you need to instantiate the controller before using it in the HTML page, as in <section ng-controller="DeviceController as deviceCtrl"&bg;.

Directive

The directive is defined by .directive('iotBar', function() {}); . The directives can be seen as helper functions.

In this context, our directive is expecting 2 parameters: progress and unit. Those parameters will be used in a basic template that will build a progress bar using Bootstrap:


<div class="progress">

<div class="progress-bar" role="progressbar" aria-valuenow="{{ progress }}" aria-valuemin="0" aria-valuemax="100" style="width: {{ progress }}%">{{ progress }}{{ unit }}</div>

</div>

I copied the template from above for ease of reading.

Conclusion

Back in 2005, I wrote an anger post called “I hate JavaScript“. At first, after years of developing and managing server-side operations, you can only agree. JavaScript is a little on the ugly side of programming language when it comes to readability. But, they are some advantages to it (one being you have no choice). AngularJS enhances the readability and maintenance of your code, allows you to avoid spaghetti-code (you can still do it, don’t worry), but has a small learning curve. However, highly recommended.