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.

A Simple App with the AngularJS Framework (Part 1)

In my last post I introduced concepts for MVC and MVVM and said I would be delving into the code for a three page application – a home page, a page that loads from a remote REST interface and an authenticated page. These three pages provide enough of a look into each framework to allow me to understand the framework and decide on which one I want to use long term. Today is all about AngularJS.

Bootstrapping AngularJS

Before we can do anything with AngularJS, we need to get it into our application. We did the same thing with Aurelia when we covered that and I fully expect to do the same thing with the other frameworks. Somehow, your page needs to know about the framework. Firstly, let’s install AngularJS:

jspm install angular

Now let’s adjust the public/index.html file to load it:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>Angular Test Site</title>
	</head>
	<body ng-app>
		<h1>Hello {{"World" + "!"}}</h1>
		<script src="jspm_packages/github/angular/bower-angular@1.4.2/angular.min.js"></script>
	</body>
</html>

The interesting code is highlighted. Firstly, let’s discuss the ng-app Angular Directive. You will sometimes see this referred to as the ngApp directive. If the directive is being used declaratively (i.e. in HTML), then it is represented as all lower case with a dash. If it’s being used in code then it’s represented as camel-case with no dash. They mean the same thing. Directives are indications that a specific behavior needs to be attached to an element. In this case, the ng-app directive tells Angular that it needs to process the body element (i.e. the whole application). If you want Angular to process only a little bit of the page, you can do that too.

Note the double curly-brackets in the h1 element. That’s an Angular Expression and allows you to embed basically any JavaScript expression inside them. More importantly, you can inject code and variables that are “in-scope” within an Angular expression. This is more useful when I talk about views – for now, just know that when you see the double curly brackets, you are looking at an Expression that will get evaluated before display.

We’ve done a whole lot of mark-up before bringing in the JavaScript. The normal thing to do these days is to load scripts right at the end of the page. When Angular loads, it looks for the directives and gets to work. You could just as easily placed the script tag in the head of the document. This is just “more normal”.

The Welcome Page

When we introduced the first page in the Aurelia tutorial, there was already a bunch of stuff around routing, controllers and views. You don’t actually need that in an Angular app – you can just start with a single page. Let’s augment our public/index.html page with the additional code to produce the Welcome 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="styles/site.css">
	</head>
	<body ng-app>
		<section ng-init="fn='Adrian';ln='Hall'">
			<h2>Enter Your Name</h2>
			<form role="form">
				<div class="form-group">
					<label for="fn">First Name</label>
					<input type="text" ng-model="fn" class="form-control" id="fn" placeholder="first name">
				</div>
				<div class="form-group">
					<label for="ln">Last Name</label>
					<input type="text" ng-model="ln" class="form-control" id="ln" placeholder="last name">
				</div>
				<div class="form-group">
					<label>Full Name</label>
					<p class="help-block">{{fn + " " + ln}}</p>
				</div>
			</form>
		</section>

		<script src="jspm_packages/github/angular/bower-angular@1.4.2/angular.min.js"></script>
	</body>
</html>

Even if you haven’t included a stylesheet, this will demonstrate the point. I have made an ad-hoc Angular Model. It’s initialized with another Angular directive – ng-init provides the initial values for the two fields. I’ve then bound those two fields to text input boxes with the ng-model directive. Finally, I’m using an angular expression to compute the full name. Start changing the text in the text-boxes and you will see the full name change along with it. This is data-binding in action.

Note that the documentation for ng-init tells you that this is a bad way to do things. You should use a controller instead. Good idea – let’s do that!

Adding Controllers

Now that I have a view working (albeit embedded into the main page), I need to develop a controller as well. Angular conventions tell me to wrap all the logic about the controller (and anything else I use) into an Angular Module. Let’s take a look at a new file: public/app.js:

"use strict";

var testApp = angular.module("testApp", []);

testApp.controller("WelcomeController", function () {
	this.fn = "Adrian";
	this.ln = "Hall";

	this.fullName = function () {
		return this.fn + " " + this.ln;
	}
});

This contains the same model parameters as before (fn and ln) and a function to calculate the full name. I want to adjust the HTML view in public/index.html so that it uses the controller instead of the embedded model now:

<!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="styles/site.css">
	</head>
	<body ng-app="testApp">
		<section ng-controller="WelcomeController as welcome">
			<h2>Enter Your Name</h2>
			<form role="form">
				<div class="form-group">
					<label for="fn">First Name</label>
					<input type="text" ng-model="welcome.fn" class="form-control" id="fn" placeholder="first name">
				</div>
				<div class="form-group">
					<label for="ln">Last Name</label>
					<input type="text" ng-model="welcome.ln" class="form-control" id="ln" placeholder="last name">
				</div>
				<div class="form-group">
					<label>Full Name</label>
					<p class="help-block">{{welcome.fullName()}}</p>
				</div>
			</form>
		</section>

		<script src="jspm_packages/github/angular/bower-angular@1.4.2/angular.min.js"></script>
		<script src="app.js"></script>
	</body>
</html>

The changed lines are highlighted. First off, our ng-app statement says “I’m an angular app and module testApp contains my stuff”. The next line says “this section uses the WelcomeController as it’s scope and I’m going to name it welcome within this section”. The ng-controller directive defines a controller association. The two input boxes have had their ng-model statements changed so that they reference the fn and ln variables within the WelcomeController. Finally, my help-block has changed. Instead of an embedded expression to calculate the full name, I’m using a function.

Running this will do the same thing as before, except now I am using a controller instead of default actions. Hopefully, you will see that writing a controller is easy and writing a view is also easy. You can embed them or separate them from your main file.

Routing

I’ve gotten to the point where I have a view (embedded in the page) and a controller (in a separate file). I want to add routing such that when I can handle multiple pages within the same application and the view and controller are separated.

This is a multi-step process. Step 1 is to add routing into the application config. I do this in my app.js file:

"use strict";

var testApp = angular.module("testApp", [ 'ngRoute' ]);

testApp.config(["$routeProvider", function($routeProvider) {
	$routeProvider
		.when("/welcome", {
			templateUrl: "partials/welcome.html",
			controller: "WelcomeController"
		});
}]);

I am declaring a dependency on ngRoute – the Angular Routing module – when I create the module. I also need to bring in the angular-route module in my main page. More on that later. Note that you don’t have to use the Angular provided router. There are others out there (most notably one called UI-Router) that provide different functionality. The core is the same though, so I’m just going to use the standard one.

Once I’ve declared the dependency on ngRoute, I can create a route-map. Right now, there is only one route – the /welcome URI maps to a template and a controller. Note how dependency-injection happens here. I am injecting the $routeProvider and then using it to generate the route-map.

To install the angular-route module, do this:

jspm install angular-route

Don’t forget to add it to the public/index.html as well – I’ll show that when I show off the new file later on.

Another place where dependency injection happens is in the route-enabled controller:

testApp.controller("WelcomeController", [ '$scope',
	function WelcomeController ($scope) {
		$scope.fn = "Adrian";
		$scope.ln = "Hall";

		$scope.fullName = function () {
			return $scope.fn + " " + $scope.ln;
		}
	}
]);

Instead of using this, I’m using a $scope variable. This allows me to access the internals of the model without using an ngController directive to associate the view to the controller.

So that’s the controller – what about the view? I’ve created a new directory: partials and created a welcome.html file within that directory to contain the view:

<h2>Enter Your Name</h2>
<form role="form">
	<div class="form-group">
		<label for="fn">First Name</label>
		<input type="text" ng-model="fn" class="form-control" id="fn" placeholder="first name">
	</div>
	<div class="form-group">
		<label for="ln">Last Name</label>
		<input type="text" ng-model="ln" class="form-control" id="ln" placeholder="last name">
	</div>
	<div class="form-group">
		<label>Full Name</label>
		<p class="help-block">{{fullName()}}</p>
	</div>
</form>

This is the contents of the section element within the main page. I’ve removed the welcome. tags on all the angular directives and expressions – those are now part of the scope and the scope is the default, so there is no reason to qualify it. My final change is to the public/index.html. I need to remove the view code and replace it with something that specifies the ng-view directive. The ng-view directive tells Angular where to place the compiled view.

<!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="styles/site.css">
	</head>
	<body ng-app="testApp">
		<section ng-view></section>

		<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>

You can see the ng-view directive at line 10. I’ve also brought in the angular-route module at line 13.

If you run this application as before, you will note that nothing is displayed. However, remember when I was doing the Aurelia tutorial that the URLs looked something like http://localhost:3000/#/welcome – put in that and you will get the same activity that we have had all along. This time it’s in a reusable controller and template view.

I can add a “default route” using an otherwise() statement in the route map:

testApp.config(["$routeProvider", function($routeProvider) {
	$routeProvider
		.when("/welcome", {
			templateUrl: "partials/welcome.html",
			controller: "WelcomeController"
		})
		.otherwise({
			redirectTo: "/welcome"
		});
}]);

This basically says “here is my route map, but if you don’t recognize anything, display the welcome page”.

Let’s extend the controller slightly. The Aurelia tutorial had a submit button – the application provided an alert when you clicked on the submit button. Let’s add that functionality:

testApp.controller("WelcomeController", [ '$scope',
	function WelcomeController ($scope) {
		$scope.fn = "Adrian";
		$scope.ln = "Hall";

		$scope.fullName = function () {
			return $scope.fn + " " + $scope.ln;
		}
		
		$scope.submit = function () {
			alert("Hello " + $scope.fn + " " + $scope.ln);
		}
	}
]);

I also need to update the view in partials/welcome.html:

<h2>Enter Your Name</h2>
<form role="form">
	<div class="form-group">
		<label for="fn">First Name</label>
		<input type="text" ng-model="fn" class="form-control" id="fn" placeholder="first name">
	</div>
	<div class="form-group">
		<label for="ln">Last Name</label>
		<input type="text" ng-model="ln" class="form-control" id="ln" placeholder="last name">
	</div>
	<div class="form-group">
		<label>Full Name</label>
		<p class="help-block">{{fullName()}}</p>
	</div>
	<button id="submit" ng-click="submit()">Submit</button>
</form>

I’m using another directive – ng-click – to wire up the event handler so that the user can click on the Submit button.

To finish up this first page, I also want to get bootstrap installed. This was just a case of using jspm to install it and then adding the CSS and JS files to the index.html file. I’ve skipped that step in these instructions, but the code is in the repository.

Adding a Second Page

Now that I have my first page configured, I want to create a second page – the flickr example from the Aurelia app. To do that, I need to add a controller and a template (easy enough given the Aurelia app has pretty much provided those for me) and then I need to wire up a route. Let’s first of all wire up the route:

testApp.config(["$routeProvider", function($routeProvider) {
	$routeProvider
		.when("/welcome", {
			templateUrl: "partials/welcome.html",
			controller: "WelcomeController"
		})
		.when("/flickr", {
			templateUrl: "partials/flickr.html",
			controller: "FlickrController"
		})
		.otherwise({
			redirectTo: "/welcome"
		});
}]);

I need to create the template view in partials/flickr.html:

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

<div class="row">
	<div class="col-sm-6 col-md-4" ng-repeat="image in images">
		<a class="thumbnail">
			<img style="width: 260px; height: 180px;" src="{{image}}"/>
		</a>
	</div>
</div>

I’m using another Angular directive here – ng-repeat – this duplicates a given block while iterating over an array. I’m using it to generate multiple thumbnails based on the array “images”. This is pretty much the same as the Aurelia version (with slightly different semantics for the repeating block). Now, on to the controller:

testApp.controller("FlickrController", [ "$scope", "$http",
	function FlickrController ($scope, $http) {
		$scope.heading = "Flickr";
		$scope.images = [];
		$scope.tags = "ranier";
		var url = "http://api.flickr.com/services/feeds/photos_public.gne";

		$http.jsonp(url, {
			params: {
				"tags": $scope.tags,
				"tagmode": "any",
				"format": "json",
				"jsoncallback": "JSON_CALLBACK"
			},
			"responseType": "json"
		}).success(function (data, status, headers, config) {
			$scope.images = data.items.map(function (v) {
				return v.media.m;
			});
		});
	}
]);

There is some very interesting stuff going on here. First of all, I am injecting the $http object via dependency injection. This allows me to do HTTP calls. I’m going to use it to call the Flickr API via JSONP. Note the jsoncallback parameter though. That’s very important. The JSONP element normally is returned via a callback which is the object wrapped in a function call. That function call must be “JSON_CALLBACK” so that the JSON object gets decoded properly. This took me quite a while to figure out. The $http methods all produce promises. However, this isn’t the Promise that I’ve come to love – it’s different. That means I call success() and error() instead of then() and catch() like normal.

Once I’ve got the data, I assign it to images (mapping each element along the way) and the data binding takes care of the rest.

Thoughts so far

Obviously, I’m going to compare the Angular version to the Aurelia version. Angular feels more fully featured than Aurelia and the community in Angular is obviously much bigger. One of the things I found particularly intriguing was the decoupling of the data source from the models. Aurelia has yet to tackle the data problem – they are concentrating on controllers and views.

However, for controllers and views (and particularly multiple controllers and views), Angular just seems much more “heavy” than the Aurelia counterpart. I wrote less code in the Aurelia version than the Angular version.

More To Come!

I haven’t finished yet, but I’ve written a lot of code and learned a lot about Angular. The Angular app was so big that I had to split it into two posts. In the next post I’ll take a look at implementing the menu bar and authentication. Stay tuned!

MVC, MVVM and Frameworks

I’ve been writing a whole bunch about MVC architectures – client side and server side.  But I hit a problem.  You see, MVC and MVVM are pretty simple concepts.  Here is a typical diagram that I see when looking at MVC descriptions:

blog-08012015-1

It’s nice and simple.  The controller loads the model and passes some form of data to the View.  The problem is this – where is the user and where is the data?  How do these actually interact?  This is actually a key point in understanding the architecture and the place that frameworks – any framework – occupies in the architecture.  I think the following is a much more representative architectural diagram:

blog-08012015-2

This makes more sense to me.  The user submits a request to a dispatcher.  The dispatcher decides which controller to pass the request to.  The controller asks the adapter to give it one or more models to complete the request.  In the case of MVVM, these models are transitioned into a View-Model, usually through some sort of data binding.  This new model (or view-model) is passed into the View rendering system, which renders the appropriate view and kicks it back to the core dispatcher so that the dispatcher can respond to the user.

It’s much more messy than the plain MVC (or MVVM) design pattern.  However, it’s implementable and I can see the pieces I need to implement in order to achieve the desired web application.  This architecture can be implemented on the client side or the server side and both sides have frameworks that assist.

Frameworks provide some sort of functionality that allow you to ignore the boiler-plate code that inevitably comes with writing such an architecture.  Most frameworks have some sort of dispatcher (normally called a router, but they do much more than that) and most frameworks have some sort of adapter logic (mostly called an ORM or Object Relational Mapper).  In between, frameworks enforce a pattern for controllers, models and views that can be used to enforce consistency across the application.

On the server side, I have two go-to languages – C# and JavaScript.  I use ASP.NET as my framework of choice on the C# server side.  I can map my visual directly to ASP.NET:

  • ASP.NET provides the dispatcher, with an ability to configure a route map in it’s startup class.
  • The Controller class can be inherited to create custom controllers
  • The Model is a plain-old class
  • The View is handled by Razor syntax
  • The Adapter is generally handled by Entity Framework.

For server-side JavaScript, the mapping is a little more messy:

I haven’t really gotten into Models and Adapters, although I can see libraries such as Mongoose (for MongoDB) playing a part there.  However, there are Node/Express MVC frameworks out there – I want to investigate Locomotive and SailsJS at some point, for example.

On the client side, things are definitely more messy.  There are a host of different frameworks – Angular, Aurelia, Ember, Knockout, Meteor, React / Flux, along with a host of others.  I’ve found the TodoMVC site to have a good list of frameworks worth looking at.  Some of these are being upgraded to handle ES2015 syntax, some are already there and some are never going to be there.

One thing to note about frameworks.  They are all opinionated.  ASP.NET likes the controllers to be in a Controllers namespace.  Angular likes you to use directives.  Aurelia likes SystemJS and jspm.  Whatever it is, you need to know those opinions and how they will affect things.

The web isn’t the only place one can use frameworks.  The MVC architecture is not limited to web development – it’s constant across applications of any complexity.  For example, you can see MVC in WinForms, Mobile applications, Mac OSX Applications and Linux Applications.

I want my application to be rendered client-side, which means I need to take a look at client-side frameworks.  My working list is:

I’m not going to bother with Backbone, Meteor, Knockout or any other older or smaller framework.  This is my own time and I don’t want to spend a ton of time on investigation.  I pretty much know what Aurelia can provide.  To investigate the others I needed a small site I could implement – something that wasn’t “just another task organizer” (TodoMVC).  To that end, I decided to create a three page application.

  • Page 1 – the home page – will get loaded initially and contain a Polymer-based carousel
  • Page 2 – a linked page – will load data from the Internet (the Flickr example from the Aurelia app)
  • Page 3 – an authenticated page – will load data from the local server if the user is authenticated

In addition to the three pages, I’m going to ensure that the navigation is separated logically from the actual pages and that – where possible – the pages are written in ES2015.  I want separation of concerns, so I expect the models to be completely separate from the views and controllers.

Each one will be implemented on top of a Node/ExpressJS server that serves up just the stuff needed.  In this way, I will be able to see the install specifics.  You can see my starter project on my GitHub Repository.  I hope you will enjoy this series of blog posts as I cover each framework.