The Magic

Alex Knol

December 2013

(For more resources related to this topic, see here.)

Application flow

In the following diagram, from the Angular manual, you find a comprehensive schematic depiction of the program flow inside Angular:

After the browser loads the HTML and parses it into a DOM, the angular.js script file is loaded. This can be added before or at the bottom of the <body> tag, although adding it at the bottom is preferred. Angular waits for the browser to fire the DOMContentLoaded event. This is similar to the way jQuery is bootstrapped, as illustrated in the following code:

$(document).ready(function(){ // do jQuery })

In the Angular.js file, towards the end, after the entire code has been parsed by the browser, you will find the following code:

jqLite(document).ready(function() { angularInit(document, bootstrap); });

The preceding code calls the function that looks for various flavors of the ng-app directive that you can use to bootstrap your Angular application.

['ng:app', 'ng-app', 'x-ng-app', 'data-ng-app']

Typically, the ng-app directive will be the HTML tag, but in theory, it could be any tag as long as there is only one of them. The module specification is optional and can tell the $injector service which of the defined modules to load.

//index.html <!doctype html> <html lang="en" ng-app="tempApp"> <head> …... // app.js ….. angular.module('tempApp', ['serviceModule']) …..

In turn, the $injector service will create $rootscope, the parent scope of all Angular scopes, as the name suggests. This $rootscope is linked to DOM itself as a parent to all other Angular scopes. The $injector service will also create the $compile service that will traverse the DOM and look for directives. These directives are searched for within the complete list of declared Angular internal directives and custom directives at hand. This way, it can recognize directives declared as an element, as attributes, inside the class definition, or as a comment. Now that Angular is properly Bootstrapped, we can actually start executing some application code. This can be done in a variety of ways, shown as follows:

  • In the initial examples, we started creating some Angular code with curly braces using some built-in Angular functions
  • It is also possible to define a controller to control a specific part of the HTML page, as we have shown in the first tempCtrl code snippet
  • We have also shown you how to use Angular's built-in router to manage your application using client-side routing

As you can see, Angular extends the capabilities of HTML by providing a clever way to add new directives. The key ingredient here is the $injector service, which provides a way to look up for dependencies and create $rootscope.

Different ways of injecting

Let's look a bit more at how $injector does its work. Throughout all the examples in this book, we have used the array-style notation to define our controllers, modules, services, and directives.

// app/ controllers.js tempApp.controller('CurrentCtrl', ['$scope', 'reading', function ($scope, reading) { $scope.temp = 17; ...

This style is commonly referred to as annotation. Each injected value is annotated in the same order inside an array. You may have looked through the AngularJs website and may have seen different ways of defining functions.

// angularJs home page JavaScript Projects example functionListCtrl($scope, Project) { $scope.projects = Project.query(); }

So, what is the difference and why are we using another way of defining functions? The first difference you may notice is the definition of all the functions in the global scope. For reference, let's call this the simple injection method. The documentation states that this is a concise notation that really is only suited for demo applications because it is nothing but a potential clash waiting to happen. Any other JS library or framework you may have included could potentially have a function with the same name and cause your software to malfunction by executing this function instead of yours. After assigning the Angular module to a variable such as tempApp, we will chain the methods to that variable like we have done in this book so far; you could also just chain them directly as follows:

angular.module('tempApp').controller('CurrentCtrl', function($scope) {})

These are essentially the same definitions and don't cause pollution in the global scope. The second difference that you may have noticed is in the way the dependencies are injected in the function. At the time of writing this book, most, if not all of the examples on the AngularJs website use the simple injection method. The dependencies are just parameters in the function definitions. Magically, Angular is able to figure out which parameter is what by the name because the order does not matter. So the preceding example could be rewritten as follows, and it would still function correctly:

// reversedangularJs home page JavaScript Projects example functionListCtrl( Project, Scope ) { $scope.projects = Project.query(); }

This is not a feature of the JavaScript language, so it must have been added by those smart Angular engineers. The magic behind this can be found in the injector. The parameters of the function are scanned, and Angular extracts the names of the parameters to be able to resolve them.

The problem with this approach is that when you deploy a wonderful new application to production, it will probably be minified and even obfuscated. This will rename $scope and Project to something like a and b. Even Angular will then be unable to resolve the dependencies. There are two ways to solve this problem in Angular. You have seen one of them already, but we will explain it further. You can wrap the function in an array and type the names of the dependencies as strings before the function definition in the order in which you supplied them as arguments to the function.

// app/ controllers.js tempApp.controller('CurrentCtrl', ['$scope', 'reading', function ($scope, reading) { $scope.temp = 17; .......

The corresponding order of the strings and the function arguments is significant here. Also, the strings should appear before the function arguments.

If you prefer the definition without the array notation, there is still some hope. Angular provides a way to inform the injector service of the dependencies you are trying to inject.

varCurrentCtrl = function($scope, reading) { $scope.temp = 17; $ = function() {$scope.temp); } }; CurrentCtrl.$inject = ['$scope', 'reading']; tempApp.controller('CurrentCtrl', CurrentCtrl);

As you can see, the definition is a bit more sizable, but essentially the same thing is happening here. The injector is informed by filling the $inject property of the function with an array of the injected dependencies. This is where Angular will then pick them up from.

To understand how Angular accomplishes all of this, you should read this excellent blog post by Alex Rothenberg. Here, he explains how all of this works internally. The link to his blog is as follows:

Angular cleverly uses the toString() function of objects to be able to examine in which order the arguments were specified and what their names are.

There is actually a third way to specify dependencies called ngmin, which is not native to Angular. It lets you use the simple injection method and parses and translates it to avoid minification problems.

Consider the following code:

angular.module('whatever').controller('MyCtrl', function
($scope, $http) { ... });

ngmin will turn the preceding code into the following:

angular.module('whatever').controller('MyCtrl', ['$scope',
'$http', function ($scope, $http) { ... }]);



In this article, we started by looking at how AngularJS is bootstrapped. Then, we looked at how the injector works and why minification might ruin your plans there. We also saw that there are ways to avoid these problems by specifying dependencies differently.

Resources for Article:

Further resources on this subject:

You've been reading and excerpt of:

Dependency Injection with AngularJS

Explore Title
comments powered by Disqus