Saturday, August 23, 2014

AngularJS Lifetime Management, Lazy-Loading, and other Advanced DI Techniques

One aspect of Angular that I love is it’s dependency injection. Contrary to some criticisms I’ve read, I find it is extremely flexible and powerful enough to address the demands of enterprise line of business apps. I discussed the general benefits of DI in Dependency Injection Explained and specifically Angular’s implementation using Providers, Services, and Factories and even aspect-oriented interception/decoration.

In this post, I address some other features common to advanced Inversion of Control containers, namely lazy-loading, lifetime management, and deferred creation/resolution.

Lazy-Loading

Lazy-loading simply refers to the late instantiation of objects when you need them. Many dependency injection systems will build-up a component the first time it is recognized as a dependency, but in some cases you may not want to instantiate the component until later in the application lifetime. In Angular, the perfect example is when you are setting up a behavior in the configuration pass that references components that haven’t been created yet.

Let’s assume you want to intercept the built-in $log service so that it stores entries on the $rootScope. I don’t recommend this but it works for a simple, contrived example. To intercept, you reference $provide in the configuration pass and then call the decorator method. If you try to reference the $rootScope directly, you’ll get an exception because of circular dependencies. The solution is to lazy-load the $rootScope instead, using the $injector.

The following code will only load the $rootScope the first time it’s needed.

$provide.decorator('$log', ['$delegate', '$injector',
    function ($delegate, $injector) {
        var log = $delegate.log.bind($delegate);
        $delegate.log = function (msg) {
            var rs = $injector.get('$rootScope');
            if (rs.logs === undefined) {
                rs.logs = [];
            }
            rs.logs.push(msg);
            log(msg);
        };
        return $delegate;
}]);

Subsequent calls will always get the same singleton instance of $rootScope. Here is the working fiddle. I’ve heard this (erroneous) criticism before (that Angular only supports singletons) … not true. The methods on the $injector are what you use to manage the lifetime of your components.

Lifetime Management

Lifetime management relates to how you handle instances of components. By default, when you inject an Angular dependency, the dependency injection will create a single copy and reuse that copy throughout your app. In most circumstances this is exactly the behavior you want. There are some solutions that may require multiple instances of the same component. Consider for a moment a counter service:

function Counter($log) {
    $log.log('Counter created.');
} angular.extend(Counter.prototype, {
    count: 0,
    increment: function () {
        this.count += 1;
        return this.count;
    }
}); Counter.$inject = ['$log']; app.service('counter', Counter);

Your app may need to keep track of different counters. When you inject the service, you always get the same counter. Is this an Angular limitation?

Not exactly. Again, using the $injector service you can instantiate a new copy any time you like. This code uses two separate counters:

app.run(['$rootScope', 'counter', '$injector',
    function (rs, c, i) {
        rs.count = c.count;
        rs.update = c.increment;
        rs.update2 = function () {
            var c = i.instantiate(Counter);
            rs.count2 = c.count;
            rs.update2 = function () {
                c.increment();
                rs.count2 = c.count;
            };
        };
    }]);

You can see each count is tracked in a separate instance in the working fiddle. If you know you are going to generate new instances often, you can register the service like this:

app.factory('counterFactory', ['$injector',
    function (i) {
        return {
            getCounter: function () {
                return i.instantiate(Counter);
            }
        };
    }]);

Then it’s simple to grab a new instance as needed, and you can reference your factory component instead of the $injector:

app.run(['$rootScope', 'counterFactory',
    function (rs, cf) {
        var c1 = cf.getCounter(),
            c2 = cf.getCounter();
        rs.count = c1.count;
        rs.update = c1.increment;
        rs.count2 = c2.count;
        rs.update2 = function () {
            rs.count2 = c2.increment();
        };
    }]);

You can check out this version by running the full fiddle here. As you can see, it is entirely possible to manage the lifetime of your components using Angular’s built-in dependency injection. But what about deferred resolution – i.e. those components you may introduce after Angular is already configured but still need to be wired up with their own dependencies?

Deferred Resolution

You’ve already seen one way you can defer the resolution of dependencies in Angular. When you want to wire something up you can call instantiate on the $injector service and it will resolve dependencies using either parameter sniffing, by looking for a static $inject property, or by inspecting an array you pass in. In other words, this is perfectly valid:

$injector.instantiate(['dependency', Constructor]);

You can also invoke a function decorated with an array as well. If you have a function that depends on the $log service, you can invoke it at run-time with the dependency resolved like this:

var myFunc = ['$log', function ($log) {
    $log.log('This dependency wired at runtime.');
}]; $injector.invoke(myFunc);

You can check out the working fiddle here (open your console to verify what happens when you click the button).

Summary

In summary, Angular’s dependency injection provides many advanced features you would expect and often require for a line of business application. The shortcut methods for factories, services, and providers sometimes confuse Angular developers into believing these are the only options available. The magic really happens on the $injector service where you can grab your singleton instance, build up a new component or dynamically invoke a function with dependencies.

As one final note, the injector is available to your client code even outside of Angular. To see an example of JavaScript code that is wired up outside of Angular yet uses the injector to grab the $log service, click here. Why is ‘ng’ passed in the array for the function? This is the core Angular module that is added implicitly when you wire up your own modules but must be explicitly included when you directly create your own instance of the injector.

Sunday, August 3, 2014

No Need to $Watch AngularJS “Controller As”

In a previous blog post I discussed how to use Angular’s extend to improve code quality and reusability. In my example I used the new controller as syntax. I see a lot of discussion online about this approach. I like it because it allows you to treat your controllers as pure JavaScript objects rather than glorified grab bags that do nothing but manipulate $scope. The biggest complaint I read is that you still have to take a dependency on $scope when you want to watch properties. Or, do you?

From lessons learned working on a large Angular project (by “large” I mean a team of 25+ developers distributed around the world, with a code base featuring 80,000+ lines of TypeScript code with hundreds of controllers, filters, and services) one mistake I made early on was depending too much on scope. For example, I might assume a detail page inherited the master page’s scope. This dependency on the hierarchy made it difficult to refactor pages, so I quickly learned that communication of properties should take place via components and services and not be based on implied scope.

Watches are similar. You have to ask yourself what you are watching for, then decide if using an actual $watch is worth it. A $watch introduces significant overhead and fires every digest loop. It will be called multiple times when other code mutates the data model, so you want to conserve your watches as much as possible. How can do you do this?

One example I covered in my Angular Debugging and Performance video. The scenario is a paged list. I have one million items in the list and am showing a few per page. You likely know from past experience with paging that there are several variables to calculate, such as how many total pages exist and where a “page” fits into the range of indexed items that represent that page. To handle the paging I created a controller that keeps track of the full list, then exposes a “display list” with the current page. Here is the basic definition with the code to generate the million entries:

function Controller() {
    var idx = 0;
    this.list = [];
    this.displayList = [];
    while (idx < 1000000) {
        this.list.push(Math.random());
        idx += 1;
    }
    this.refreshPages();
}

The controller keeps track of several variables, and they are all recomputed in the refreshPages function that should fire any time the current page changes. Here is the initial definition of the controller and that method:

angular.extend(Controller.prototype, {
    pageSize: 20,
    _currentPage: 1,
    nextPage: 2,
    previousPage: 0,
    currentIndex: 0,
    totalPages: 0,
    refreshPages: function () {
        var curIdx = this.currentIndex;
        this.totalPages = Math.ceil(this.list.length / this.pageSize);
        this.currentIndex = this.pageSize * (this._currentPage - 1);
        this.previousPage = this._currentPage - 1;
        this.nextPage = this._currentPage + 1;
        if (curIdx !== this.currentIndex || this.displayList.length === 0) {
            while (this.displayList.length > 0) {
                this.displayList.pop();
            }
            for (curIdx = this.currentIndex;
                 curIdx < this.list.length && curIdx < this.currentIndex + this.pageSize;
                 curIdx += 1) {
                this.displayList.push(this.list[curIdx]);
            }
        }
    }
});

You may notice the current page is defined as a private property. Why not expose it and then use a $watch? The answer is simple. I control my model, so I don’t need to watch it to know when it is mutated. Instead, I can take advantage of pure JavaScript and expose the current page as a property that updates the necessary variables whenever it changes. After that happens, I quickly clear out the exposed array for the page and repopulate it based on the newly computed variables. Here is the solution that doesn’t involve adding an unnecessary watch but instead uses Object.defineProperty:

Object.defineProperty(Controller.prototype, "currentPage", {
    enumerable: true,
    configurable: true,
    get: function () { return this._currentPage; },
    set: function (val) {
        this._currentPage = val;
        this.refreshPages();
    }
});

Now the property is defined on the controller using pure JavaScript, and can be bound like any other property. Here is markup for the button to navigate to the previous page. It simply decrements the currentPage property and is disabled when you are on the first page.

<button data-ng-click="ctrl.currentPage = ctrl.currentPage-1"
          data-ng-disabled="ctrl.currentPage == 1">
    &lt;
</
button>

If I wanted to optimize even further, I could add an additional condition to ensure the value is actually different before refreshing the pages. The result is a huge list paged efficiently using “controller as” syntax. You can see the running example here: long paged list in AngularJS. The full source is here: long paged list in AngualrJS source code.

The bottom line: I prefer the “controller as” syntax because it allows me to define controllers as pure JavaScript objects with minimal dependencies. I don’t have to explicitly concern myself with $scope and data-binding, and instead can treat the controller itself as a view model and know the exposed properties are available for binding.

Instead of using $watch I use built-in JavaScript features to manage my model. This has the added advantage of making the component easier to test (in this example, you can test the controller without any dependency on Angular whatsoever). If I am concerned about the value changing from another controller, I set up a service for communication rather than adding the watch and again maintain control over the changes without the overhead of being called every digest loop.

$watch my latest video to learn more about AngularJS Debugging and Performance.

Sunday, July 27, 2014

Using AngularJS to Extend Your Code Quality

The AngularJS API provides a function named extend that can help improve your code quality and efficiency. I always look for ways to improve quality, increase efficiency, reduce risk and eliminate ritual and ceremony when I am developing software. Perhaps the simplest way to express this is the DRY principle (Don’t Repeat Yourself). I prefer a refactoring-driven approach to this principle. Instead of trying to anticipate what might be needed in a framework, I simply evolve it and refactor when I see an opportunity to improvement. Oftentimes Angular’s extend function is a part of that refactoring.

Assume I am writing a page that allows the user to click two buttons to produce a list of categories and products. Here is a screenshot with the categories loaded and the products waiting for the user to request them.

image

The source for data is exposed via the example API at OData.org. To encapsulate the call for categories, I create a component and register it with Angular that looks like this:

function CategoriesService($http, $q) {
     this.$http = $http;
     this.$q = $q; } 




CategoriesService.prototype.get = function () {
     var deferral = this.$q.defer();
     this.$http.get('http://services.odata.org/V4/OData/OData.svc/')
         .success(function (response) {
             deferral.resolve(response.value);
         })
         .error(function (err) {
             deferral.reject(err);
         });
     return deferral.promise; }; app.service('categories', CategoriesService);

Next, I move on to products. It turns out that the products service looks almost identical to the categories service! I don’t want to repeat myself so it’s time to refactor the code to take advantage of the principle of inheritance. I encapsulate the base functionality for dealing with the service in a base class, then inherit from that and specify what’s unique between products and categories. The base class looks like this:

var baseOData = {
     $http: {},
     $q: {},
     baseUrl: 'http://services.odata.org/V4/OData/OData.svc/',
     entity: '',
     get: function () {
         var defer = this.$q.defer();
         this.$http.get(this.baseUrl + this.entity)
             .success(function (response) {
                 defer.resolve(response.value);
             })
             .error(function (err) {
                 defer.reject(err);
             });
         return defer.promise;
     } };

Notice I’ve captured everything that is repeated: the base portion of the URL and the wrapper that handles the promise so the result is returned and the consuming class doesn’t have to understand how the collection is implemented in the API. The only difference I found between the categories and products is the name of the entity specified in the URL, so I expose that with a property on the base class that can be overridden by the service implementation. Using this base class I implement the category service like this:

function CategoriesService($http, $q) {
     this.$http = $http;
     this.$q = $q;
     this.entity = 'Categories'; } angular.extend(CategoriesService.prototype, baseOData); app.service('categories', CategoriesService);

The shell for the categories service simply sets up the dependencies and registers the entity because the base class holds all of the common functionality. The call to extend automatically applies the properties and functions from the base definition to the category service. Notice that I am extending the prototype; this will ensure that the properties and functions are part of any instance that is created. Angular will also bind the properties and functions so this refers to the instance itself.

The products service is then implemented the same way with a different entity specified. Although I could provide a service that takes in the entity as a parameter and returns the promise (even less code), I may want to have specific properties or methods that are unique to the category and/or product implementation. I really don’t know yet so I keep them as separate components and will refactor them down to a single service if the pattern doesn’t change.

You can call extend multiple times or pass a collection of objects. This enables your components to inherit from multiple “base classes.” Another way to look at it is that you can define behaviors and apply those behaviors to the class.

I prefer the “controller as” syntax for my controller definitions. In this example the controller takes a dependency on the product and category service and exposes methods to request them that are bound to buttons. The initial implementation looked like this:

function Controller(products, categories) {
     this.productService = products;
     this.categoryService = categories;
     this.products = [];
     this.categories = []; } Controller.prototype.getCategories = function () {
     var _this = this;
     this.categoryService.get().then(function (result) {
         _this.categories = result;
     }); }; Controller.prototype.getProducts = function () {
     var _this = this;
     this.productService.get().then(function (result) {
         _this.products = result;
     }); }; app.controller('exampleCtrl', Controller);

Wouldn’t it be nice if to encapsulate the controller functionality in a single definition? Actually, that is possible! Using extend I simplified the declaration for my controller by combining the functions into a single object definition. I removed the initialization of the product and categories list from the constructor and moved them into a consolidated definition that looks like this:

angular.extend(Controller.prototype, {
     products: [],
     categories: [],
     getCategories: function () {
         var _this = this;
         this.categoryService.get().then(function (result) {
             _this.categories = result;
         });
     },
     getProducts: function () {
         var _this = this;
         this.productService.get().then(function (result) {
             _this.products = result;
         });
     } });

This convention makes it easier to group related functionality together and ensure there is a consistent implementation of this. The implementation is very similar to the way that TypeScript handles inheritance.

As you can see, although the documentation for extend is quite simple, the functionality can be quite powerful. View the source code and full working example here.