Lazy Loading Javascript in AngularJS

TL;DR Explanation

You can lazy load JavaScript for controllers, directives, and services in AngularJS through use of careful structuring of your declarative objects and making a slight change to the way you may traditionally declare AngularJS components. This is more convenient (and conceptually cleaner) than holding onto references to services used during AngularJS configuration/bootstrapping phases.

The Common Way

I’ve been using AngularJS a lot as of late and I found myself wanting to lazy load some of the JavaScript for my controllers, directives, and services so that they are only downloaded if someone clicks through to that particular route. As I use AngularJS routing, I came across the common problem of needing to declare all my controllers, directives, and services before AngularJS does it’s bootstrapping of my application otherwise you get some errors about controllers etc. being undefined. Obviously it’s hard to do this if my JavaScript hasn’t even yet been downloaded.

After doing a bit of a search online I came pass a method that appears to be pretty popular but IMHO it felt a little clunky. It’s been well documented at the following links and appears to have originated from a post by Ifeanyi Isitor.

  1. http://ify.io/lazy-loading-in-angularjs/ (appears to be the original post)
  2. http://www.bennadel.com/blog/2553-loading-angularjs-components-after-your-application-has-been-bootstrapped.htm
  3. http://www.codeproject.com/Articles/838402/Lazy-loading-directives-in-AngularJS-the-easy-way

This is entirely an opinion – but to me this method feels like it breaks the conceptual integrity of AngularJS by maintaining a reference to the service providers beyond the initial config. I don’t have any real proof that this opinion is anything more than a feeling (I would need to go deep into the AngularJS source to find out for certain), but nonetheless it just didn’t appear correct to me.

Additionally I didn’t really like the idea of changing the methods I use to declare my controllers, directives, and services. Other than it being a slight pain if I want to remove lazy loading (and I can’t imagine a reason why I would need to) there isn’t really anything specific I can point to that makes this method wrong, it just doesn’t feel right to me. For me it’s like saying, “The rat the cat the dog chased killed ate the malt.”, rather than just saying, “The rat ate the malt.”. Although the first sentence is grammatically fine, it just doesn’t feel right.

So with this in mind I decided to come up with a better way. I think I have – but I’ll leave that to you to decide.

The Less Awkward Method (IMHO)

I’ve created a sample project (I use WebStorm for web development which may explain why I have referenced files as I have) which you can download from my personal GitHub account at https://github.com/chris-tomich/mymemorysucks/tree/master/LazyLoadingAngularJS. For this example I use RequireJS (http://requirejs.org/) to do the actual loading for me so feel free to use whatever you feel most comfortable with.

So the critical problem in lazy loading AngularJS controllers, directives, and services is that the declarations for those components need to be done before AngularJS begins it’s “bootstrapping” phase. If you attempt to do the declarations after bootstrapping the application you’ll get errors about your AngularJS components being undefined etc. and none of it will work (I’m simplifying this but hopefully you get the point / experienced the error messages).

Rather than keep a reference to the various services used during bootstrapping (as done in the previously mentioned articles), my method is to continue to declare the controllers, directives, and services using the standard means but rather than declaring the meat of the code with the declaration, declare it as a separate object/factory in a different JavaScript file. The following illustrates an example of how I traditionally would have structured my code and then an example of how I can re-structure my declaration into two files to achieve the same thing.

Traditional Method – No lazy loading of JavaScript

Traditionally Declared Controller

Separated Class Method – Lazy Loading of JavaScript Supported

About Controller Declaration

 

About Controller

 

As a result of this separation, the bulk of the code for your controller, directive, or service will now reside in separate file/s. The only thing left to do now is to make sure these are marked as a dependency and loaded when they are requested by the routeProvider. You do the loading of the dependency the same way used in other posts which is through the “resolve” attribute during configuration of your routes. Below is an example of how I’ve done it in my sample project.

Route Provider Declaration

 

I’ve tested this method both in 1.2.28 and 1.3.15 and it seems to work fine. Although the samples I’ve shown here are for controllers, it is exactly the same for directives and services in that you separate out the meat of your code into a different file and then call that object/function from within the declaration. To see those types of components in action download my sample project at https://github.com/chris-tomich/mymemorysucks/tree/master/LazyLoadingAngularJS.

One other benefit of using this method is that in a scenario where you want to get rid of the lazy loading, it doesn’t require much re-coding. It’s just a matter of changing the routeProvider declarations and adding the <script> tag declarations for all your scripts.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s