AngularJS: Adding One Resolve to All Routes

$routeProvider doesn’t come with the functionality to include a resolve in every single route on an application. But universal resolves can be added in using angular.extend(), an angular method that copies one object into another.

A quick refresher on resolves in Angular routes: a resolve is a dependency that should be injected into the controller for a route. The main difference between using a resolve and just requiring the dependency in the controller is that promises in resolves are completed before Angular renders the controller. If there are any errors in completing the promise, the $routeChangeError event triggers. Resolves take the complexity of completing a promise and checking for errors out of the controller itself. (You can read more on $routeProvider and resolves in the Angular docs.)

How angular.extend Works

angular.extend() takes an infinite amount of parameters, and copies each param onto the one before it. So, given:

var A = {foo: 'bar', fizz: 'buzz'};
var B = {foo: 'baz'};
angular.extend(A, B);

console.log(A); would print out Object {foo: "baz", fizz: "buzz"}.

You can use this to extend $routeProvider.when() to always include a particular variable in the resolve.

angular.extend in Action

My hypothetical use case is an interactive fiction game that needs information on the spells the player knows in every view. A working demo of this example is hosted on Plunker.

I’ve found it cleanest to put all of this code under app.config(), right before configuring the routes. First, create an object containing any/all of your universal resolves:

myApp.config(["$routeProvider", function($routeProvider) {
  var universalResolves = {
    "spells": function() {
      return [
        { word: "xyzzy", response: "Nothing happens." },
        { word: "open sesame", response: "The door opens!" }
      ];
    }    
  };

Then, extend $routeProvider.when(). I created a new version of .when() that added my universal resolves to route.resolve, and then passed the path and new route to the original$routeProvider.when().

The new .when() also returns itself, to allow chaining of multiple calls on our new route provider — without the return this;, the function would return the original$routeProvider.when() for all calls after the first, and the custom method would only be used for the first route.

  var customRouteProvider = angular.extend({}, $routeProvider, {
    when: function(path, route) {
      route.resolve = (route.resolve) ? route.resolve : {};
      angular.extend(route.resolve, universalResolves);
      $routeProvider.when(path, route);
      return this;
    }
  });

From then on, adding universal resolves is as simple as configuring your routes. The only difference being that now you want to call your route config functions on your new, extended version of$routeProvider.

  customRouteProvider
    .when('/home', {
      controller: "HomeCtrl",
      templateUrl: "_home.html",
      resolve: {
        "user": function() {
          return { name: "Player 1" };
        }
      }
    })
    .when('/cast/:spell', {
      controller: "CastCtrl",
      templateUrl: "_cast.html"
    })

    .otherwise({
      redirectTo: '/home'
    });
}]);

And that’s all there is to it, universal resolves using angular.extend().