Promises, Promises Pt. 4
Angular Syntax: Gettin' Dirty

Okay, this’ll be quick. This article is just for those using Angular, especially those versions before Angular 2. I figure that even if the entire world stopped writing new apps in Angular, there would still be a metric pantload of legacy apps to maintain. This article assumes enough familiarity with Angular to create controllers and services. The reason I’m writing this article is that Angular has one particular quirk about timing: dirty-checking. You can’t just change data; you gotta tell Angular you did it so that it can go through the laborious task of updating the view.

Fortunately, this is fairly simple. Angular has documentation for $q, their take on Kris Kowal’s Q. When promises from Angular resolve or reject, they trigger the scope to refresh, which updates the view.

$q

Let’s start with the $q library. It’s very easy to bring in, since it’s part of the default “ng” module. You can either use $q.resolve or $q.reject to create a promise from a value or an error. $q.resolve is an alias for $q.when to match ES6 naming standards.

var app = angular.module('app', []);
app.controller('MainCtrl', ['$q', MainCtrl]);
function MainCtrl($q){
	var vm = this; // I use controller as syntax
	var passPromise = $q.resolve(3);
	var failPromise = $q.reject("Nonsense!");
	passPromise.then(function(val){
		vm.goodVal = val;
	});
	failPromise.catch(function(err){
		vm.errorMessage = err;
	});
}

There is also another method of creating promises: creating a deferred with $q.defer(). The idea of a deferred is that it’s like a promise that you can change programmatically. Then you release a promise from the function. The deferred is hidden within the scope, but its external promise becomes an indicator of it.

function MainCtrl($q){
	var vm = this;
	// Add a waiting variable that adds a loading icon to the view
	vm.waiting = true;
	var luckyPromise = getLuckyPromise();
	luckyPromise.then(function(val){
		// success callback
		vm.goodVal = val;
	}, function(err){
		// failure callback
		vm.errorMessage = err;
	}, function(){
		// either way, lets get rid of the loading icon.
		delete vm.waiting;
	});

	// Create a promise that fails or passes based on a random value
	function getLuckyPromise(){
		var deferred = $q.defer();
		setTimeout(function(){
			// Get a random value between 0 and 3
			var val = Math.floor(Math.random() * 4);
			if(val == 0){
				deferred.resolve(val);
			} else {
				deferred.reject("It was zero!");
			}
		}, 2000);
		return deferred.promise;
	}
}

And $q can be used the same as the Promise constructor, which is good if you want your code to be easily refactored later. One thing I have noticed often in my JavaScript career is that I’ll depend on a library that fills a gap in the code (such as jQuery, Underscore, or Flash), and then the native APIs will catch up or there’ll be a superior framework, rendering that library obsolete. Writing code that comports with the coming standards is a good way to ensure that switching your code over later will be easier.

function getLuckyPromiseWithQConstructor(){
	return $q(function(resolve, reject){
		setTimeout(function(){
			// Get a random value between 0 and 3
			var val = Math.floor(Math.random() * 4);
			if(val == 0){
				resolve(val);
			} else {
				reject("It was zero!");
			}		
		}, 2000);
	});
}

And speaking of the ES6 standard, $q.all() and $q.race() perform the same functions described in the last post.

$http

This is really where my experience with promises in Angular started. $http (click here for the docs) encapsulates making http requests like $.ajax in jQuery. In fact, let’s do the basic run-through of how to get stuff from a web API to the controller.

var app = angular.module('app', []);
app.service('GroupSvc', ['$http', GroupSvc]);

function GroupSvc($http){
	return {
		getGroups: getGroups
	};

	function getGroups(){
		return $http.get('http://whatever.com/api/groups')
			// Transform a successful response
			// from a promise of a httpResponse
			// to a promise of the data
			.then(function(response){
				return response.data;
			});
	}
}
app.controller('MainCtrl', ['GroupSvc', MainCtrl]);

function MainCtrl(GroupSvc){
	var vm = this;
	vm.gettingGroups = true;

	GroupSvc.getGroups().then(function(groups){
		vm.groups = groups;
	}, function(error){
		vm.errorMessage = error;
	}, function(){
		delete vm.gettingGroups;
	})
}

The Group Service abstracts away the interaction with the web API, and the Main Controller just consumes promises from the service and updates the view based on that. When it comes to 80% of apps in production, you’re going to be doing a lot of this. But there’s another way of making promises that does a great job of helping you prototype and get quick feedback to see if what you’re building works.

$timeout

In some of the examples above, I created the “lucky promise” using $q and setTimeout. $timeout does a great job of making that code quicker.

	// Create a promise that fails or passes based on a random value
	function getLuckyPromiseWith$timeout(){
		return $timeout(function(){
			// Get a random value between 0 and 3
			var val = Math.floor(Math.random() * 4);
			if (val == 0) throw "It was zero!"
			return val;
		}, 2000);
	}

If the code throws an exception, the promise rejects; if the code returns a value, the promise resolves with that value. Now, this is a very contrived example. Where would you actually use this functionality?

There are lots of scenarios that call for $timeout, but the one that I faced the most was just demonstrating that my UI behaved as expected. I always liked coding from the UI backwards; that way I had an easier time getting feedback from clients, and an easier time verifying that what I was doing would work in the first place. After all, user interaction is not a static thing. It takes place in the physical space of the view, but also in time. By using a timeout of about 2 seconds, I was often able to show the UI that I had planned in a way that made sense to non-technical people or end users. Let’s say that I’m making a cloud-based todo app, and the first thing that happens after the end user signs in is that it loads whatever tasks that that user has for the day. Well, a good way to fake that is to create a couple of dummy tasks and return them from a 2 second timeout.

var todoApp = angular.module('todoApp', []);
todoApp.controller('MainCtrl', ['$timeout', MainCtrl]);

function MainCtrl($timeout){
	var vm = this;
	vm.loadingTodos = true;
	$timeout(function(){
		var currentDate = new Date()
		return [
		{
			title: "Take Out Recycling", 
			dueDate: (new Date()).setDate(currentDate.getDate() - 1);
		}, {
			title: "Take dog to vet",
			dueDate: (newDate()).setDate(currentDate.getDate() + 1);
		}];
	}, 2000)
		.then(function(todos){
			vm.todos = todos;
		}, function(err){
			vm.errorMessage = err;
		}, function(){
			// Either way, delete the spinner
			delete vm.loadingTodos;
		});
}

And here’s a simple template for this data

<div ng-controller="MainCtrl as main">
	<!-- Only show this loader if there are no todos -->
	<i class='fa fa-spinner spin' ng-if='main.loadingTodos'></i>
	<!-- Only show this if there's an error -->
	<p ng-if='main.errorMessage'>
		Caution! <span ng-bind='main.errorMessage'></span>
	</p>
	<!-- If there's valid data, show this. -->
	<ul ng-if='main.todos && main.todos.length'>
		<li ng-repeat='todo in main.todos'>
			<span ng-bind='todo.title'></span>
			-
			<span ng-bind='todo.dueDate'></span>
		</li>
	</ul>
</div>

Okay, so let’s say that you’ve put this in front of a client or a manager or a somebody and they like it, and they want you to move forward. Well, you know that eventually you’ll end up putting that promise in an external service that will call a web API, but the guy that makes the web API isn’t around, or maybe he’s just not there yet, and you have work to do. So let’s at least move that crazy logic out of the controller.

var todoApp = angular.module('todoApp', []);

todoApp.service('TodoSvc', ['$timeout', TodoSvc]);

// Create a service with the signature you need
function TodoSvc($timeout){
	return {
		getTodos: getTodos
	};

	// Returns a promise of an array of todo objects
	function getTodos(){
		// This code is cut and paste from the controller
		return $timeout(function(){
			var currentDate = new Date()
			return [
			{
				title: "Take Out Recycling", 
				dueDate: (new Date()).setDate(currentDate.getDate() - 1);
			}, {
				title: "Take dog to vet",
				dueDate: (newDate()).setDate(currentDate.getDate() + 1);
			}];
		}, 2000);
	}
}

todoApp.controller('MainCtrl', ['TodoSvc', MainCtrl]);

function MainCtrl(TodoSvc){
	var vm = this;
	vm.loadingTodos = true;

	// Now, this controller doesn't need any more cleanup.
	TodoSvc.getTodos()
		.then(function(todos){
			vm.todos = todos;
		}, function(err){
			vm.errorMessage = err;
		}, function(){
			// Either way, delete the spinner
			delete vm.loadingTodos;
		});
}

So, now your controller logic is cleaned up, the API interaction abstracted away. Sure, your service layer is still a thin tissue of lies, but as soon as the API guy gets his act together, it’s an easy switch out.

function TodoSvc($timeout){
	return {
		getTodos: getTodos
	};

	// Returns a promise of an array of todo objects
	function getTodos(){
		// Finally, a proper $http call.
		return $http
			.get('/api/todos')
			.then(function(response){
				return response.data;
			});
	}
}

You can see how easy it is to pretend that you have the right functionality. You are now free to test your interaction layer without needing to have the whole thing figured from top to bottom.

I’ve covered how you can use $q, $http, and $timeout in Angular to build and test asynchronous interactions. In the next post, I’m going to run through a few real-world scenarios I’ve faced that required constructing complex promises.

"index-pl.html"