Dependency annotations

AngularJS needs assistance to know what to inject into the callbacks we provide to certain functions, such as controller(). In the examples we’ve seen so far, AngularJS uses the argument name, so if we were to make a typo such as £scope AngularJS wouldn’t know what to inject.

Although typos are possible, they’re fairly quick to spot (via errors in your browser’s developer tools/console) and fix. A more significant problem is associated with the minification of scripts comprising our AngularJS applications. We’ve all seen the output of most minifers: unintelligble single-character variable names everywhere! Minifers will rename $scope, for example, and AngularJS won’t get any injection assistance.

To be able to use minifers, we can use dependency annotations as follows:

In the above snippet, we’re now passing an array as the second parameter tocontroller(). The last item in the array needs to be our original callback, while the first items are the string names of the dependencies to be injected. A rudimentary minifier might output the above snippet as follows:

Strings can’t be renamed, so AngularJS can use dependency annotations to get the injection assistance it needs. There’s a small caveat to this approach: the array item order and callback argument order must be in sync. Additionally, we’re still prone to errors from typos, but overall the benefit of minification far outweighs these caveats in any larger application.

However, we won’t use dependency annotations in this tutorial, preferring instead to keep snippets lean and focused.

Custom injectables

AngularJS offers four techniques for registering our own injectable components, much like $scope, as follows:

  • constant
  • value
  • factory
  • service

Injectable components are known as services in AngularJS speak, so it’s a shame that they decided to call one of the techniques the same.

We will discuss the more common techniques that I have used myself and have seen used by others, namely value and factory. Constant and service are less common and more complicated in use; as such, I consider them beyond the scope of this tutorial.

Value services

A value service allows us to register injectable values (be it strings, numbers, objects, arrays or functions). They allow us to define a value that can’t be changed, much like a constant in other programming languauges. The fact that there is also a constant service is another unfortunate naming choice by AngularJS. The difference between the two is associated with the configuration life cycle of an AngularJS application, rather than anything to do with the value registered.

A value service allows us to register a value once and use it many times (via injection). Consider the limitTo filter from the “Directives again” section of this tutorial. We could introduce a pageSize value service, inject it into articleCtrl and use it as the default number of articles to display. pageSize could also be reused in other areas of the application, such as administering a subset of a large number of categories.

Edit the contents of services.js as follows:

In the above snippet, we create the most basic of value services, registering the value 2 for the pageSize injectable component.

Edit the contents of controller.js as follows:

In the above snippet, articleCtrl now has a dependency on pageSize, so the value 2 is injected in. We then store it as $scope.numArticles for use in our HTML.

Finally, edit index.html as follows:

In the snippet above, limitTo is part of an expression and can therefore include$scope properties. We’ve replaced the hard-coded number of articles to limit to with numArticles. Additionally, we’ve attached ng-model to a new HTML text input. This is to allow a user to override the default provided by pageSize, should they wish to do so.

Factory services

A factory service also allows us to register injectable values. The key difference is that the injectable value is the return value of a function that can itself be injected with dependencies. This is definitely best explained with an example.

Edit services.js as follows:

 

In the above snippet, we have registered calculateCategoryPercentage as a value service that will calculate a percentage of used categories from an array from articles. The details of how (i.e., the nested looping) isn’t important, but note how we have hard-coded the available categories in a local variable.

Now edit services.js as follows:

In the snippet above, we have registered the previously hard-coded available categories as a value service, enabling them to be used elsewhere in the application via injection. We have then registered calculateCategoryPercentage as a factory service, a function that gets called once with its dependency (availableCategories) injected and then returns the same function as the value service version.

To see how to use calculateCategoryPercentage, edit controllers.js as follows:

In the snippet above, calculateCategoryPercentage is injected into the controller and then invoked with our hard-coded list of articles. It’s important to note that availableCategories could also be injected into the controller, and in a later section we will do so.

As with other examples we’ve seen, $scope.categoryPercentage can be used inindex.html with a simple binding, such as: