In the past I’ve written code that looked like this:

angular.module('myApp')
    .factory('user', ['$http', '$auth', '$q', 'api', function user($http, $auth, $q, api) {

    var user = null;
    var loadingUser = false;
    var deferred = $q.defer();

    return {
        getCurrent: function () {
            if (!$auth.isAuthenticated()) {
                deferred.reject('User is not signed in.');
            }
            else if (user) {
                deferred.resolve(user);
            } else if (loadingUser) {
                return deferred.promise;
            } else {
                loadingUser = true;
                $http.get(api.currentUser()).then(function (response) {
                    user = response.data;
                    loadingUser = false;
                    deferred.resolve(user);
                }, function (error) {
                    loadingUser = false;
                    deferred.reject(error.data);
                });
            }
            return deferred.promise;
        }
    };
}]);

We have a simple user factory with a single function, getCurrent(). It checks to see if the user is logged in and if they are, it goes and gets the information about them. The way it’s been written, user.getCurrent() can be called in multiple locations simultaneously and only generate a single HTTP request. When an app initially fires up, it could easily have several controllers or components that require user information, so this is a fairly common need in the world of JavaScript callbacks. You wouldn’t want to generate 3 HTTP requests to the same API resource simply because your app had 3 locations in which it was required.

The funny thing, however, is that you don’t need all of this fancy code in order to ensure only one HTTP request is generated. You just need to set the cache to true (it defaults to false).

angular.module('myApp')
    .factory('user', ['$http', '$auth', '$q', 'api', function user($http, $auth, $q, api) {

    var deferred = $q.defer();

    return {
        getCurrent: function () {
            if (!$auth.isAuthenticated()) {
                deferred.reject('User is not signed in.');
            } else {
                $http.get(api.currentUser(), {cache: true}).then(function (response) {
                    deferred.resolve(response.data);
                }, function (error) {
                    deferred.reject(error.data);
                });
            }
            return deferred.promise;
        }
    };
}]);

Things are simpler this way. There’s less logic to have to think about, and less room for mistakes.

We could simplify this still further, however, when we consider the following:

  1. The chances are high that we’ll want to check $auth.isAuthenticated() in the location that calls this user.getCurrent() function.1 It’s convenient having the safeguard inside this factory, but the reality is that something probably shouldn’t be calling this function if it doesn’t know whether the user is logged in or not.
  2. Whatever calls user.getCurrent() will need callbacks for the success and failure scenarios. We’re doing callbacks in this function to lightly massage the data before returning it, but other than that, we’re not really doing anything.

Let’s use this information to further simplify our user factory.

angular.module('myApp')
    .factory('user', ['$http', 'api', function user($http, api) {

    return {
        getCurrent: function () {
            return $http.get(api.currentUser(), {cache: true});
        }
    };
}]);

This is much leaner than our original factory! The only two things that have changed from the calling function’s standpoint are:

  1. It will need to $auth.isAuthenticated() before it calls user.getCurrent(). It was probably already doing this anyway.
  2. It will need to refer to the data property of the callback object. This could be a nuisance depending on how frequently the callback is used throughout the code, which is why the second factory might be a good compromise.

I wish that I’d read Angular’s $http docs thoroughly back when I started learning Angular. The cache property is invaluable for resources that aren’t expected to change.2

When learning a new technology, it’s often difficult to find the balance between reading a bunch of new documentation that is easily forgotten and jumping into the code and learning by building. Both reading documentation (or watching well put together screencasts) and actual practice are important.


  1. In case you’re wondering, $auth is more than a mere dummy example. It’s valid syntax for satellizer. ↩︎
  2. The value that it is set to could be a variable instead of a boolean literal, so when you do expect it to change, you could flip it back to the default of false. ↩︎