I recently tried AngularJS for a pet project. I watched a great tutorial about the platform, then dove in head first. You can see what I built here: MysterySolver

I enjoyed Angular. It was straightforward to use, and allowed me to bang out a lot of functionality without much cumbersome boilerplate code. Jasmine, the testing framework set up in the bootstrap source, was also pretty slick. I really liked how I could nest a bunch of test blocks inside of each other to reuse common setup code.

The only serious bump I ran into was getting multiple controllers to work together.

My goal was to build a wizard-style flow. A user enters a bit of info, hits a button, then enters more info. The answers in one step affect the questions in future steps, or might cause steps to be added or taken away. I wanted the controller to trigger the navigation, and I wanted to pass state when it did.

View-led navigation would have been easy: add a link whenever you like. A user clicks and the new controller is loaded. This kind of navigation is great for keeping controllers ignorant of each other. I suspect this approach is better for search engine indexers as well. It wasn’t ideal for me.

I searched the Internet and read a bunch of documentation hoping to find an easy answer. What I found was a bunch of other people asking the same questions. When I finally decided to build it myself, the solution was easier than I expected:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
'use strict';

// var module = angular.module('...', []);
module.service('navigation', function($location) {
var storage = null;

return {
navigate: function(path, data) {
storage = {
path: path,
data: data
};
$location.path(path);
},

getNavigationData: function() {
if ((storage == null) || (storage.path != $location.path())) {
throw 'navigated without passing data';
}

var data = storage.data;
storage = null;
return data;
}
};
};

To navigate, I used code like this:

1
2
3
4
5
module.controller('Page1Controller', ['$scope', 'navigation', function($scope, navigation) {
$scope.navigate = function (navigationParameter) {
navigation.navigate('/Page2', navigationParameter);
};
}]);

In the destination controller, I fetched the navigation parameter like this:

1
2
3
module.controller('Page2Controller', ['$scope', 'navigation', function ($scope, navigation) {
$scope.navigationParameter = navigation.getNavigationData();
}]);

It was easy to test this code. Whenever I expected a controller to navigate, I checked the value of $location.path(). To pass navigation parameters into controllers, I just called the navigate method before the controller was created in the setup block.

Unfortunately this solution breaks the back button. Because the browser triggers backward navigation, the navigation parameter won’t be set when the controller tries to load. This wasn’t something I needed, so I left alone.