A Simple App with the AngularJS Framework (Part 2)

In my last post I introduced some major concepts in AngularJS development. I handled models, views and controllers and set up routing. There were a couple of things I did not do, however. The first is the menu bar and the second is authentication. Let’s cover the menu bar first.

The Menu Bar

There are lots of ways to configure a menu bar and I don’t think any one is any better than the others. I could create a custom Angular Directive that takes a DIV and turns it into my nav bar based on a template and a controller. This would be good if I were going to re-use the menu bar time and again.

Alternatively, maybe this is so common that someone else has already done it. There is a great web site – ngmodules.org – that contains a collection of Angular modules that are ready to use. This wouldn’t assist me in learning Angular.

Instead, I’m going to add my code to the main public/index.html file and create another controller. The view will be embedded in the main public/index.html file. Here is the code for the main page:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>Angular Test Site</title>
		<link rel="stylesheet" href="jspm_packages/npm/font-awesome@4.3.0/css/font-awesome.min.css">
		<link rel="stylesheet" href="jspm_packages/github/twbs/bootstrap@3.3.5/css/bootstrap.min.css">
		<link rel="stylesheet" href="styles/site.css">
	</head>
	<body ng-app="testApp">
		<header ng-controller="NavBarController">
			<div id="brand">
				<div class="valign-o">
					<div class="valign-i">
						<i class="fa fa-home"></i>
						<span>{{title}}</span>
					</div>
				</div>
			</div>
			<div id="navigation">
				<ul>
					<li ng-class="{ active: isActive('/welcome')}">
						<a href="#/welcome">Welcome</a>
					</li>
					<li ng-class="{ active: isActive('/flickr')}">
						<a href="#/flickr">Flickr</a>
					</li>
				</ul>
			</div>
			<div id="toolbar">
				<ul>
					<li id="navProfile"></li>
					<li id="navSignIn">
						<i class="fa fa-sign-in"></i>
					</li>
				</ul>
			</div>
		</header>

		<div class="page-host">
			<section ng-view></section>
		</div>

		<script src="jspm_packages/github/components/jquery@2.1.4/jquery.min.js"></script>
		<script src="jspm_packages/github/twbs/bootstrap@3.3.5/js/bootstrap.min.js"></script>
		<script src="jspm_packages/github/angular/bower-angular@1.4.2/angular.min.js"></script>
		<script src="jspm_packages/github/angular/bower-angular-route@1.4.2/angular-route.min.js"></script>
		<script src="app.js"></script>
	</body>
</html>

I pretty much copied the additional markup from my Aurelia example. There are some minor tweaks to account for the difference in syntax for including specific code. In this case, I am using the isActive() method within the controller to determine whether to add the active class or not. I’ve included the sign-in toolbar and will get to that in the next section – it’s not necessary for the navigation to work. I use a named controller with the ng-controller directive to wire up my controller to this view.

If I compare this to Aurelia, then I did the same thing in the app.html file – that file became the template for my overall page. In Angular, the root document is the template for my overall page.

I already know how to create a controller, so let’s take a look (thanks to myl on Stack Overflow for this code). I added this to the app.js file:

testApp.controller("NavBarController", [ "$scope", "$location",
	function NavBarController ($scope, $location) {
		$scope.title = "Angular";
		
		$scope.isActive = function (viewLocation) {
			return viewLocation === $location.path();
		};
	}
]);

This does what is expected. I would love, however, to have the nav-bar separated. To do that, I need to write a custom Angular Directive. Here is the code within app.js:

testApp.directive("ngNavbar", function () {
	return {
		restrict: "A",
		templateUrl: "partials/navbar.html"
	}
});

This defines an Attribute (restrict: "A") that I can place on another HTML element. When I put the ng-navbar attribute on that element, its innerHTML will be populated with the template I have specified. I need to create that navbar.html file:

<header>
	<div id="brand">
		<div class="valign-o">
			<div class="valign-i">
				<i class="fa fa-home"></i>
				<span>{{title}}</span>
			</div>
		</div>
	</div>
	<div id="navigation">
		<ul>
			<li ng-class="{ active: isActive('/welcome')}">
				<a href="#/welcome">Welcome</a>
			</li>
			<li ng-class="{ active: isActive('/flickr')}">
				<a href="#/flickr">Flickr</a>
			</li>
		</ul>
	</div>
	<div id="toolbar">
		<ul>
			<li id="navProfile"></li>
			<li id="navSignIn">
				<i class="fa fa-sign-in"></i>
			</li>
		</ul>
	</div>
</header>

This is exactly the same code as was wrapped inside the public/index.html file. Now I can remove that code from the public/index.html file:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>Angular Test Site</title>
		<link rel="stylesheet" href="jspm_packages/npm/font-awesome@4.3.0/css/font-awesome.min.css">
		<link rel="stylesheet" href="jspm_packages/github/twbs/bootstrap@3.3.5/css/bootstrap.min.css">
		<link rel="stylesheet" href="styles/site.css">
	</head>
	<body ng-app="testApp">
		<div ng-controller="NavBarController" ng-navbar></div>

		<div class="page-host">
			<section ng-view></section>
		</div>

		<script src="jspm_packages/github/components/jquery@2.1.4/jquery.min.js"></script>
		<script src="jspm_packages/github/twbs/bootstrap@3.3.5/js/bootstrap.min.js"></script>
		<script src="jspm_packages/github/angular/bower-angular@1.4.2/angular.min.js"></script>
		<script src="jspm_packages/github/angular/bower-angular-route@1.4.2/angular-route.min.js"></script>
		<script src="app.js"></script>
	</body>
</html>

This makes the code much more readable. I could also place the controller in the angular directive I’ve just created as well – a task for another time.

Authentication

As I am doing for the other projects, I’m going to use Auth0 for authentication here. Auth0 has provided an excellent tutorial for this purpose, so I am only going to note the differences.

Installing the libraries was mostly done with jspm:

jspm install angular-cookies
jspm install github:auth0/angular-storage
jspm install github:auth0/angular-jwt

The public/index.html file became the following:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>Angular Test Site</title>
		<link rel="stylesheet" href="jspm_packages/npm/font-awesome@4.3.0/css/font-awesome.min.css">
		<link rel="stylesheet" href="jspm_packages/github/twbs/bootstrap@3.3.5/css/bootstrap.min.css">
		<link rel="stylesheet" href="styles/site.css">
	</head>
	<body ng-app="testApp">
		<div ng-controller="NavBarController" ng-navbar></div>

		<div class="page-host">
			<section ng-view></section>
		</div>

		<script src="jspm_packages/github/components/jquery@2.1.4/jquery.min.js"></script>
		<script src="jspm_packages/github/twbs/bootstrap@3.3.5/js/bootstrap.min.js"></script>
		<script src="jspm_packages/github/angular/bower-angular@1.4.2/angular.min.js"></script>
		<script src="jspm_packages/github/angular/bower-angular-route@1.4.2/angular-route.min.js"></script>
		<script src="jspm_packages/github/angular/bower-angular-cookies@1.4.2/angular-cookies.min.js"></script>
		<script src="jspm_packages/github/auth0/angular-storage@0.0.11/dist/angular-storage.min.js"></script>
		<script src="jspm_packages/github/auth0/angular-jwt@0.0.9/dist/angular-jwt.min.js"></script>
		<script src="http://cdn.auth0.com/js/lock-7.5.min.js"></script>
		<script src="http://cdn.auth0.com/w2auth0-angular-4.js"></script>
		
		<script src="app.js"></script>
	</body>
</html>

That’s lots of scripts at this point – I’d probably handle this with Browserify or some other sort of bundling technology in a production app. This is good for right now. Note that I’m downloading a couple of the libraries from the Auth0 CDN – I’d probably look into bringing these in as local libraries as well. Auth0 publishes lock on their GitHub page.

The partials/navbar.html page also needed a change – I’ve highlighted the changed lines:

<header>
	<div id="brand">
		<div class="valign-o">
			<div class="valign-i">
				<i class="fa fa-home"></i>
				<span>{{title}}</span>
			</div>
		</div>
	</div>
	<div id="navigation">
		<ul>
			<li ng-class="{ active: isActive('/welcome')}">
				<a href="#/welcome">Welcome</a>
			</li>
			<li ng-class="{ active: isActive('/flickr')}">
				<a href="#/flickr">Flickr</a>
			</li>
		</ul>
	</div>
	<div id="toolbar">
		<ul>
			<li id="navProfile">{{nickname}}</li>
			<li id="navSignIn" ng-click="login()">
				<i class="fa fa-{{icon}}"></i>
			</li>
		</ul>
	</div>
</header>

Lines 22 and 24 bind specific variables in the $scope of the NavbarController to these areas. Line 23 uses the ng-click event handler to call the login method in the NavbarController.

Moving on to the app.js, there are two things I must do. Firstly, I have to configure the module and Angular application for authentication. This is straight from the Auth0 Quick Start Guide:

var testApp = angular.module("testApp", [ 'ngRoute', 'auth0', 'angular-storage', 'angular-jwt' ]);

testApp.config(["$routeProvider", "authProvider", function($routeProvider, authProvider) {
	// Route Map
	$routeProvider
		.when("/welcome", {
			title: "Welcome",
			templateUrl: "partials/welcome.html",
			controller: "WelcomeController"
		})
		.when("/flickr", {
			title: "Flickr",
			templateUrl: "partials/flickr.html",
			controller: "FlickrController"
		})
		.otherwise({
			redirectTo: "/welcome"
		});

	authProvider.init({
		domain: "DOMAIN.auth0.com",
		clientID: "YOUR-CLIENT-ID"
	});
}]);

// This hooks all auth events to check everything as soon as the app starts
testApp.run(function (auth) {
	auth.hookEvents();
});

Line 3 brings in all those libraries that Auth0 said I needed. Line 5 adds the authProvider via dependency injection. The authProvider is provided by Auth0. Line 22-25 actually configures the Auth0 settings. Remember to inject your own domain and clientID here. Finally, line 29-31 ensures all the authentication events are captured as the application starts.

The other area of concern is the NavbarController. Here is the new one:

testApp.controller("NavBarController", [ "$scope", "$http", "auth", "store", "$location",
	function NavBarController ($scope, $http, auth, store, $location) {
		$scope.title = "Angular";
		$scope.nickname = "";
		$scope.icon = "sign-in";

		$scope.isActive = function (viewLocation) {
			return viewLocation === $location.path();
		};

		var profile = store.get("profile");
		if (profile) {
			$scope.nickname = profile.name || profile.nickname;
			$scope.icon = "sign-out";
		};

		$scope.login = function() {
			if ($scope.icon === "sign-out") {
				$scope.nickname = "";
				$scope.icon = "sign-in";
				auth.signout();
				store.remove("profile");
				store.remove("token");
			} else {
				auth.signin({}, function (profile, token) {
					// Success callback
					store.set("profile", profile);
					$scope.nickname = profile.name || profile.nickname;
					store.set("token", token);
					$scope.icon = "sign-out";
					$location.path("/");
				}, function () {
					// Error callback
					alert("Auth0 Error!!!!");
				});
			}
		}
	}
]);

There are two new scope variables. The $scope.nickname will be blank when the user is not logged in and will be filled with something when the user is logged in. The $scope.icon will either be “sign-in” or “sign-out” – this changes the icon that is displayed.

From line 82-86, I set up the initial values of the nickname and icon based on the localStorage (represented by the store variable). The default case is the user is unauthenticated. As a result, I only need to change things if the user is authenticated – i.e. when something is in the profile.

The $scope.login() method is called when the user clicks on the authenticator icon. If the user is signed in, then the user is logged out. If the user is not signed in, then I pop up the Auth0 sign-in window and wait for soemthing to come back. A successful return means that the user is logged in, so I store the token and profile and update the nav bar view.

An Authenticated Page

In my Aurelia review, I added an authenticated page. This page only appeared in the navigation when I was authenticated. It also grabbed data from the backend and displayed it. I wanted the same thing here. First of all, I need an authenticated Web API – changes to server.js:

var express = require("express"),
	jwt = require("express-jwt"),
	morgan = require("morgan"),
	staticFiles = require("serve-static"),
	spells = require("./spells.json"),
	config = require("./config.json");

var app = express();

// Check that the JWT is signed by us
var jwtCheck = jwt({
  secret: new Buffer(config.clientSecret, 'base64'),
  audience: config.clientID
});

// Set the port to listen on
app.set("port", process.env.PORT || 3000);

// Set up logging
app.use(morgan("combined"));

// Set up static files within public
app.use(staticFiles("public"));

// Set up a JWT Check for the /api/spells layer
app.use("/api/spells", jwtCheck);

// Set up the spells endpoint
app.get("/api/spells", function (request, response) {
	response.send(spells);
});


// Listen on the TCP port
app.listen(app.get("port"), function () {
	console.log("Listening on port " + app.get("port"));
});

The spells.json file will be returned when I send an authenticated request to the backend for URI /api/spells. An authenticated request is a request with a valid JSON Web Token in the Authorization header. Your config.json should look like this:

{
	"domain": "YOUR-DOMAIN.auth0.com",
	"clientID": "YOUR-CLIENT-ID",
	"clientSecret": "YOUR-CLIENT-SECRET"
}

Ensure you replace the appropriate values from your configuration in Auth0. Now that I have an authenticated API, I need to display it. Firstly, let’s create a new route for “/spells” to go to the new page:

testApp.config(["$routeProvider", "authProvider",
	 function($routeProvider, authProvider) {
		// Route Map
		$routeProvider
			.when("/welcome", {
				title: "Welcome",
				templateUrl: "partials/welcome.html",
				controller: "WelcomeController"
			})
			.when("/flickr", {
				title: "Flickr",
				templateUrl: "partials/flickr.html",
				controller: "FlickrController"
			})
			.when("/spells", {
				title: "Spells",
				templateUrl: "partials/spells.html",
				controller: "SpellsController"
			})
			.otherwise({
				redirectTo: "/welcome"
			});

		authProvider.init({
			domain: "YOUR-DOMAIN.auth0.com",
			clientID: "YOUR-CLIENT-ID"
		});
	}
]);

Lines 19-23 add the new route to the spells page. I also need a Controller and a View. The Controller is as follows:

testApp.controller("SpellsController", [ "$scope", "$http", "auth", "store",
	function SpellsController ($scope, $http, auth, store) {
		$scope.heading = "Spells";
		$scope.spells = [];
		var url = "/api/spells";
		var headers = {
			"Accept": "application/json"
		};

		// Add the authorization token if we are authenticated
		if (auth.isAuthenticated) {
			headers.Authorization = "Bearer " + store.get("token");
		}

		$http.get(url, {
			"withCredentials": true,
			"responseType": "json",
			"headers": headers
		}).success(function (data, status, headers, config) {
			$scope.spells = data;
		}).error(function () {
			console.error("Error retrieving spells");
		});
	}
]);

The “magic” (such that it is) is in lines 89-91 – if the user is authenticated, I add an Authorization header with the JSON Web Token in it. That is sent with the normal headers. Once the response comes back, I assign the result to the spells variable in the scope. My partials/spells.html view is simple:

<h2>{{heading}}</h2>

<div id="spellList">
	<ul>
		<li ng-repeat="spell in spells">
			{{spell.id}}
		</li>
	</ul>
</div>

The spells variable in the scope is an array. I iterate over that array, pulling out the ID. You should get a bullet list of numbers when this is working.

There are a couple of problems with this version of the code (although it does work). Firstly, the Spells link shows up even when the user is not authenticated. This means that you will see the error message in the console. Secondly, the user gets logged out on page refreshes. This is less than ideal. Fortunately, Auth0 has provided a recipe for the latter issue. Place the following in your app.js file:

// This hooks all auth events to check everything as soon as the app starts
testApp.run([ "$rootScope", "auth", "store", "jwtHelper", "$location",
	function ($rootScope, auth, store, jwtHelper, $location) {
		auth.hookEvents();

		$rootScope.$on("$locationChangeStart", function() {
			var token = store.get("token");
			if (token) {
				if (!jwtHelper.isTokenExpired(token)) {
					if (!auth.isAuthenticated) {
						auth.authenticate(store.get("profile"), token);
					}
				} else {
					$location.path("/");
				}
			}
		});
	}
]);

This code replaces the testApp.run() method. It’s a little different than the version provided by Auth0 – this version takes advantage of the dependency injection in AngularJS v1.4. The version provided by Auth0 is for AngularJS v1.2.

This leaves making the Spells link disappear in the navigation. I already have the auth variable available in the NavBarController so I can create a new isAuthenticated() method and use it the same way as I did the isActive() method:

		$scope.isAuthenticated = function() {
			return auth.isAuthenticated;
		};

Now I can adjust the partials/navbar.html to change the Spells link to be authenticated.

<li ng-class="{ active: isActive('/spells'), 'auth-hide': !isAuthenticated()}">
  <a href="#/spells">Spells</a>
</li>

The auth-hide class is defined in styles/site.css to set display: none.

Note how I check authentication in multiple places. This goes to the “don’t trust the user” principal. I ensure the user can’t see the Spells area. If, however, the user has bookmarked the URI, then I ensure that the authentication is not passed to the backend and hence the data is not shown. If I were being particularly paranoid, I would ensure that a different message was shown and the request was not sent to the backend.

Wrapping Up

That’s it for the Angular test. Over the course of the last two posts, I’ve created controllers, views, routing, authentication and custom directives. This is definitely more complex than Aurelia, but it also has a bigger community, better support (including books, videos, tutorials) and more features. If I were deciding on something other than my own project, I would likely use AngularJS over Aurelia. (This is, of course, before I have investigated Ember, Meteor or React/Flux).

There is a big thing coming though. Angular 2 is the “next version of Angular” and promises to utilize all the features of ECMAScript 6. This warrants it’s own investigation once the code base is a little further along. However, it’s a forklift upgrade, although the Angular team says they will support “Incremental” updates by installing both Angular 1.x and 2.x at the same time. It doesn’t sound ideal.

If you want the code from my small tutorial here, it’s on my GitHub Repository.