Category: AngularJs

AngularJs Tip: Don’t use $watch on $scope

Try to use “ng-change” instead of “$watch” when you want to watch a particular value. There are couple of benefits like performance improvement because of no overhead on digest cycle & writing test cases for ng-change is very easy compared to writing them on $watch.

Advertisements

AngularJs: Understanding loading Modules – II

This is my last post in the series to understand workflow for loading modules into Angular, Dependency Injection. Here is the workflow diagram created by me. This is as per my understanding of the concepts. Feel free to correct me if I’m wrong.angular_module_overview1

AngularJs: Understanding Loading modules

Banging my head for 2 days gave me some insight into how AngularJs loads modules. In the previous posts I have been talking about $injector, $provider, DI, createInjector bla bla bla.. All the information I have shared researching online material gave me bits and pieces of information but not a complete picture. Then I have decided to go through source code, with the knowledge gained from previous research I was able to understand how it’s achieved.

This post is going to be a bit longer. concepts like $injector, $provider, DI are touched in this post, which are covered in detail in my previous posts.

*Click on images to open in new tab for reading explanation for the code.

So let’s begin the show 🙂

When we write a simple angular program –  There are lot many questions that might be araising in our head

  1. When was core ng-module loaded
  2. How AngularJs was able to fetch my dependencies like $location, $window, $scope etc.. by just passing string into array or function
  3. How our module dependencies are loaded

and lot more.. By End of this post you will get answers to most of your questions.

First of all angular.js is a self invoking function which takes two parameters window & document. When we include this script file in html all the code is executed once and variables are set if any. This will help AngularJs to publish it’s internal methods which can be accessed via keyword angular.

Example: angular.extend, angular.bootstrap, angular.injector, angular.toLowerCase etc..

angular is added as property to window object whose value is empty object.

angular = window.angular || (window.angular = {})

Since angular is a self invoking function, at the end of script file there are three methods called

  1. bindJQuery()
  2. publishExternalAPI(angular) – makes available list of commonly used methods globally.
  3. angularInit(document, bootstrap) – This will bootstrap module provided on document via ngApp or using angular.injector().

Before looking into publishExternalAPI and angularInit, let’s have look at setupModuleLoader function

This is called within publishExternalAPI and bootstrap to create moduleInstance, like what needs to be returned when module is created and called. I have placed my comments directly in the source code for better understanding. Refer image below for more details on setupModuleLoader

SetupModuleLoad

In short, setupModuleLoader will add  module to angular.module.modules object with moduleInstance contaning controller, filter, provider, factory, value, constant, config run, _invokeQueues and _runBlocks these 2 queues get filled when invoke function is called on any of the above functions are called on module, refer image for better understanding.

publishExternalAPI: This is the most interesting and one of the core modules that drives AngularJs. I’m explaining all the important points right beside code for easier understanding. Enlarge picture for more informationPublish External API

Till now publishing methods on angular to external world is done and there are two modules added to angular.module.modules section

  1. ngLocale
  2. ng – This module has configFn where all the setup related to inbuild services & directives is there.

angularInit(document, bootstrap): angularInit is the place where bootstrapping is done, This is where module loading and Dependency Injection starts. After some initial startup angularInit finally calls bootstrap function on the element.

Bootstrap: After some initial setup bootstrap calls doBootStrap function which finally returns our eagerly waiting $injector on successful loading of modules. Enlarge picture for more details on what doBootstrap does. Do Bootstrap

Bootstrap calls createInjector method with list of modules that needs to be loaded. This includes ng module as well, what does createInjector method with the array of modules given to it ?

createInjector – This is where actually modules are loaded, before loading modules there is another setup createInjector does for us. It creates 2 cache objects for us

  1. providerCache – contains 2 objects
    • $provider – with six methods on it provider, value, constant, service, factory and decorator.
    • $injector
  2. instanceCache – contains $injector method on it and this injector will be returned by createInjector method.

Let’s see what exactly code does in createInjector.Create InjectorI’m not going to discuss in detail about what createInternalInjector does . I have already covered that in detail in my previous post. In short createInternalInjector returns a object which contains five methods on it

  1. invoke – invokes original method by calling javascript’s apply on it
  2. instantiate – creates new service object.
  3. getService – fetches the service from cache
  4. annotate – process the input function and returns array of dependencies that need to be injected
  5. has – checks whether the service present or not in cache.

so only part left is loadModules, let’s explore it. click on image to open in new tab to read comments beside code for more information.

loadModules

This is how modules are loaded into AngularJs Application. This is a very big post and might be confusing as well because of not using proper english. Share your questions via comments and I would be happy to help

 

AngularJs Tip: bootstrap !!

  1. Using ngApp will auto bootstrap your application.
  2. Only one AngularJs application can be auto-bootstraped for one html document. To run multiple applications in an html document we must manually bootstrap them using angular.bootstrap
  3. AngularJs applications cannot be nested within each other.

AngularJs: Loading modules

Loading modules is the most important step. Let’s see how it’s done !!

function loadModules(modulesToLoad){
    var runBlocks = [], moduleFn, invokeQueue, i, ii;
    forEach(modulesToLoad, function(module) {
      if (loadedModules.get(module)) return;
      loadedModules.put(module, true);

      try {
        if (isString(module)) {
          moduleFn = angularModule(module);
          runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);

          for(invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) {
            var invokeArgs = invokeQueue[i],
                provider = providerInjector.get(invokeArgs[0]);

            provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
          }
        } else if (isFunction(module)) {
            runBlocks.push(providerInjector.invoke(module));
        } else if (isArray(module)) {
            runBlocks.push(providerInjector.invoke(module));
        } else {
          assertArgFn(module, 'module');
        }
      } catch (e) {
        if (isArray(module)) {
          module = module[module.length - 1];
        }
        if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
          // Safari & FF's stack traces don't contain error.message content
          // unlike those of Chrome and IE
          // So if stack doesn't contain message, we create a new string that contains both.
          // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
          /* jshint -W022 */
          e = e.message + '\n' + e.stack;
        }
        throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
                  module, e.stack || e.message || e);
      }
    });
    return runBlocks;
  }

runBlocks and invokeQueues are two important aspects in module loading.

A module in angular is setup as follows:

angular.module(‘myModule’, [dependency]);

First we retrieve the module object using the angularModule function. When the module is setup two arrays are populated runBlocks and invokeQueues.

runBlocks

This is an array that contains list of run blocks angular.module(‘test’, []).run which needs to be run immediately after modules loaded.

invokeQueue

These are populated with each service that is added to the module using the familiar angular.module('myModule').controller, angular.module('myModule').directive

Each item in the queue is an array with three elements. The first is the provider that will invoke the service, the second is the method on the provider to use and the third element is an array of any arguments passed to the service.

Let’s see example:

angular.module('test', [])
		.controller('testController', function($scope) {
			console.log('setting up controller');
		});

When controller is setup like this invokeQueue for this controller looks like [‘$controllerProvider’, ‘register’, [‘test’, function($scope) {…}]] 

$controllerProvider – built-in Angular provider that enables registering controllers.

register – This will add service to the list of available controllers. Note that nothing gets added to the injectors cache. This means controllers cannot be injected into a service. If you really needed to get access to a controller (you do when unit testing) you would inject the $controller provider and retreive the controller by calling get(controllerName).

AngularJs: Double Injectors !!

When createInjector is called to load modules, there are two injectors created they are providerInjector & instanceInjector, createInjector will return instanceInjector method. Lets look at the implementationinjectorsTwo parameters are passed into createInternalInjector method cache and factory function. Cache is used for lookup and factory function is used to instantiate service when not found in cache.

InstanceInjector: It stores the list of instantiated services, whereas cacheInjector stores the list of uninstantiated services. When createInternalInjector is called the second parameter passed is a factory function. It will try to get the service by appending ‘provider’ suffix and then invokes the $get function on provider and returns the result.

providerInjector: cache for providerInjector is initialized with one service $provider. It is through this service that all other services are registered. Details of provider implementation are present here.

Now that we understood how createInjector will resolve dependencies,. Let’s see how it loads modules in the next post.

AngularJs: Revisiting createInjector

In the previous posts we have seen how DI is achieved using $provider and $injector. In this post we go further details of how exactly dependencies are injected into angular. It all starts when module gets loaded. It calls createInjector method with parameter modules to load.

createInjector(modulestoLoad) returns an object called $injector with following methods instantiate, get, has, invoke and annotate. we have gone through details in previous post. Let’s see what exactly each method does in real.

  1. Annotate: Angular must know which dependencies needs to be loaded, annotate will take care of this. It will return an array of dependencies that needs to be injected. Refer below image for more details.Annotate implemenation
  2. Invoke: will call actual implementation of the method by calling javascript’s apply() method by getting all the required informationinvoke implementation
  3. getService: Based on the string it got, it tries to fetch the service from cache, I will talk about cache in next post. If it finds it in cache, it returns the service else instatiate the serivce using factory method, adds it to the cache and returns the service.getservice implementation
  4. Instantiate: It will create a new constructor function for the service and returns the newly created instantiated object.instantiate