Late registering
&
Lazy loading

Gonzalo Ruiz de Villa Suárez
@gruizdevilla

Demo project

You may find the sources at:

https://github.com/gonzaloruizdevilla/angularlazyload

To start the project:

    $ npm install
    $ bower install
    $ npm start
    




By default,
AngularJS does not allow the registry of new resources after the app starts:



However, you may access the pieces that can register resources in the config phase.



If you expose them, in a somehow artificial way, you may use then later to register new resources.

Exposing these utilities for later registering of resources


module.config(
    function (
        $controllerProvider, 
        $compileProvider,
        $filterProvider, 
        $provide
    ) {
        module.lazy = {
            controller: $controllerProvider.register,
            directive: $compileProvider.directive,
            filter: $filterProvider.register,
            factory: $provide.factory,
            service: $provide.service
        };
   }
);

You may register new resources after the app has started:



module.lazy.factory('MySrv', function () {
    //factory body
});

However, unit tests can get a little more complicated. You can prevent this if you register services this way:



(module.lazy || module).factory('MySrv', function () {
    //factory body
});
    

When unit testing, karma loads all scripts before starting, therefore registering all the resources before starting the app.

Even better doing this:

module.config(
    function (
        $controllerProvider,
        $compileProvider,
        $filterProvider,
        $provide
    ) {
        module.controller = $controllerProvider.register;
        module.directive = $compileProvider.directive;
        module.filter = $filterProvider.register;
        module.factory = $provide.factory;
        module.service = $provide.service;
    }
);
In the config phase, we replaces methods that become useless with the new ones that allows late registering. The app then is completely unchanged, even when adding this feature afterwards in the project development.

Limitations


  • No official support for these techniques. No guarantee that they will be compatible with next versions of AngularJS 1.*.
  • As the config phase ended and the app is already running, then:
    • You can't configure the new loaded resources. Therefore, it has no sense to register lately with the 'provider' method.
    • You can't decorate the new resources.
    • You can't add new modules.

However, in my experience, these limitations weren't important for the resources I wanted to register lazily.

Lazy loading


It is very important to find the best fitting convention for your project: it will minimize the configuration needed for lazy loading.
Tip: rely on the route configurations. Add in the 'resolve' property a function that returns a promise that will be resolved then the dependencies are loaded.

Script.js


Por the example, I use script.js, a very lightweight (1.5kb) library for async loading of scripts


bower install script.js --save


https://github.com/ded/script.js

To prepare the route's configuration:



function resolveDependencies(config, dependencies) {
  config.resolve = {
    dependencies: function ($q, $rootScope) {
      var deferred = $q.defer();
      $script(dependencies, function () {
          $rootScope.$apply(function () {
              deferred.resolve();
          });
      });
      return deferred.promise;
    }
  };
}


Using RequireJS


The same principle, but a little more complex
and powerful due to AMD
and RequireJS nature:

  • Pasa la referencia al módulo de AngularJS
    mediante los mecanismos de RequireJS
  • The AngularJS app must be started manually
    (app.bootstrap), because it is RequireJS
    who knows when the resources have finished
    loading.


To RequireJS or not to RequireJS


  • RequireJS is much much more powerful,
    but it adds complexity you need to take into account:
    • Configuration: dependencies are defined twice in each file: inside 'define'  and when registering the resource in  AngularJS. Without RequireJS,  you may have this duplicity too (as in the example), but you can remove all or part of it with conventions.
    • Testing  is more complex with RequireJS. With the first technique, testing is done as usual.





¡Thanks!

Late registering and lazy load in AngularJS (en)

By Gonzalo Ruiz de Villa

Late registering and lazy load in AngularJS (en)

Late registering and lazy load in AngularJS

  • 9,323