21. March 2016

Lazy Angular: Writing Scalable AngularJS Apps

Lazy Angular: Writing Scalable AngularJS Apps

There are two major issues I have faced in the past few years, when writing AngularJS applications, and I have seen numerous other teams fighting the same battles. Out of these experiences the “Lazy Angular” approach came to life. It gives us a project structure which works for both, large and small applications. And it enables us to keep a somewhat consistent load time as new features come to life and our app grows.

Project structure

The whole setup is based on the angular-lazy Yeoman generator. There, you will also find a description of the whole toolchain that comes with it and what each library is used for.

The project structure follows a component based approach. To keep Angular apps – or client side applications in general – maintainable, we should split them up in self-contained components. This also allows us to reuse UI elements across projects easier. Other frameworks like React or Polymer already push this approach more prominently to developers and it has proven to be the most convenient so far. Angular 2 is heading in the same direction and the upcoming version 1.5, which is currently in beta, will also introduce the component method. It is an addition to existing helper methods like directive or service and follows the familiar builder pattern used to define Angular modules. It enforces best practices like using controllerAs instead of working directly on the $scope object. You can read more about it on the official AngularJS blog.

The generator differentiates between four component types:

  1. Application component: This type of component is used as an entry point for an AngularJS app. It contains our routes configuration and provides application wide settings. It is basically used to put together the puzzle pieces of our project. Usually, we will only have one application component per project. But there might be scenarios where we could have two apps which share a lot of components, e.g. a public and admin section. In this case we might want to put both apps in the same project since their code base overlaps heavily.
  2. State component: These components contain our application states. Behind them is UI Router, which is currently the de-facto standard router. So you can use all the functionality of it within state components. One could ask why we do not make the states part of the application component since they are very specific to it? We will explore the reasoning behind it later in this article.
  3. General component: These are our reusable UI building blocks. The template for those relies on Angular's component provider and gives us an easy way to create custom elements.
  4. Directive component: Since we cannot create custom attributes with Angular's component provider, the generator includes a template for attribute directives to cover that gap.

Loading only what is needed

By default, Angular expects all parts of an application to be present when the bootstrapping process starts. This is feasible for smaller projects with few dependencies. But as the project grows, so will its loading time. At some point in time, it will take too long to load all code upfront and the user experience will suffer. And that is where the lazy part of “Lazy Angular” comes into play.

We already have split our application into smaller components. Now we need to teach Angular to load those only when they are needed. Luckily, Oliver Combe has already done a great job in this area with ocLazyLoad. It wraps the core components in a layer which enables us to inject additional modules after the actual application was bootstrapped.

Next comes UI Router. To configure our states, we would need to load the code behind them with all its dependencies. But again, that long tail would lead us to a situation where we would load most of our app code upfront. To get around this, we need to split the declaration and implementation of our states. Lucky us, there is a neat little library called UI Router Extras, which allows us to do exactly that. It extends UI Router with a feature called “Future States”. These are states which can be late bound. Think of them as variables which you declare when the application starts, but you assign them a value at a later point in time. We can use a simple JSON file to declare our states.

    "name": "app",
    "url": "/",
    "type": "load",
    "src": "components/application/index"
    "name": "app.home",
    "url": "home",
    "type": "load",
    "src": "components/home-state/index"

The name and url properties are the same we use to configure our states in UI Router. The type tells UI Router Extras which loader to use to retrieve that state. If you need to load some states differently than others you can introduce new types and configure the future state provider accordingly. The loading mechanism for the states is part of the angular-lazy package which automatically gets installed when you generate a new application. Finally, the src property tells the loader where the state component can be loaded from.

Lazy Loading Flow.

In the above illustration, you see roughly how the lazy loading of states works. If the user clicks on a link, UI Router will first try to find the target in it’s list of states. If the state is found, it means that it is already loaded and can be displayed immediately. If UI Router cannot find it, it will trigger a $stateNotFound event. UI Router Extras picks this up and searches within it’s list of future states. If a matching future state has been configured, then it will be loaded and shown afterwards.

Now, no matter if our project has five or fifty states, we only have to load the application component and the first state, e.g. the login screen or the home page, to show the running app to the user. The other parts will be loaded as the user navigates through the screens. The user agent now only needs to retrieve the parts of the app which are relevant to the user and thus saves memory and cache space. When rolling out a new release, we only need to update the components which were adapted and let the browser serve the other parts from the cache where possible. Loading an entire app for each user is usually a waste, since they might not need all of the functionality.

Optimizing network performance

Soon you will realize, that you end up with a lot of small files when you use the angular-lazy generator. Each requires a network request to load. The overhead each request introduces lets the load time suffer. But better loading performance was the main reason why we chose this approach in the first place. So how can we rectify that again? Let us think about it.

We know that when we load our application component, we will not only need the Angular module itself, but also the connected templates, configurations, route declarations etc. In cases where we have static dependencies like this, we would ideally load all parts at once. The same is true for some of our 3rd-party libraries. At the time we load Angular we also need at least UI Router, UI Router Extras and ocLazyLoad, to set up everything on the client side. With angular-lazy-bundler we can do exactly this without writing an extensive configuration. Since it can rely on the project structure given by the generator, the bundler can handle components automatically. It even supports nested components. The only thing you have to define manually is, which libraries to combine. A minimal application component looks something like this:

|    +config
|    |    constants.json
|    |    default-locale.js
|    |    error-handling.js
|    |    routes.json
|    |    routing.js
|    application.html
|    application-controller.js
|    application-route.js
|    index.js

The bundler will then go ahead and combine all pieces into one application.js file. Under the hood, it uses SystemJS Builder to find all parts it needs to combine. With that in place, we can load the application component with only one request instead of nine.

Example application

I recently gave a workshop on the whole approach and created an example app to show how an application based on “Lazy Angular” could look like. If you run the app you will see that whenever possible components are loaded asynchronously.

In the above screenshot, you see that the Readme component is loaded way after the index state, which contains the first screen the user sees. Since the Readme component uses Bootstrap to show the file contents, it is also loaded lazily. This way we have to load 300 KB (30 KB compressed) less data to show the user the first screen. The repository state component is not loaded at all, since the user has not accessed it yet.

To reduce the number of network calls required to load the application, we are going to merge files which always belong together next. For that we run gulp bundle in the project folder.

This brings the number of network calls from 128 down to 69, of which most are made to load user avatars. If you only look at the application resources, we now only have 6 instead of 69 requests. An average 3G connection has a round-trip time of 100ms. This means that the bundling saves us up to 6 seconds of load time.


Having a tried and tested project structure and toolchain to start with, we have more time analysing and implementing our customers needs. It is definitively an opinionated stack and it is not the holy grail for all projects, but I am sure a lot of projects can benefit from it.

Mato Ilic
Mato Ilic

Web developer since the dark ages of Netscape Navigator. Never lost the passion for it.

Related Articles

Why Podman is worth a look

When it comes to managing containers, Docker is often the first choice. But Podman can also be an alternative. In this blog post, we take a closer look at the differences between the two tools and the points you need to consider if you plan to use Podman.

find more information
Skalierung 750x410
Skalierung von Agilität im Unternehmen

find more information
Axpo Agile Workshop Lead Image
Wie man mit dem Bau eines Marsrovers agiles, skaliertes Vorgehen lernt

Wie man agiles Arbeiten auch spielerisch erlernen kann, haben wir bei der Axpo gezeigt. Wir bauten einen Marsrover mit agilen Methoden. Was gar nicht so einfach war, jedoch gut gemeistert wurde.

find more information
Robo Advisor Sparkonto 750x410
Warum Robo Advisor an Sparkonten scheitern

Robo Advisor stellen inzwischen keine Innovation mehr dar. Bereits vor über 10 Jahren wurde davon gesprochen und seit 2009 ist der erste digitale Berater in der Schweiz im Einsatz. Bisher wurde aber der physische Berater nicht ersetzt, noch konnte die grosse Masse der Schweizer Sparkontobesitzer zum Anlegen bewegt werden. Wie also können Robo Advisor einen Mehrwert für den Endkunden bieten?

find more information
Taking Agile to the Next Level
The End of All Projects, or: Taking Agile to the Next Level

Imagine a world without projects, project leaders and product owners. Imagine a broader product definition, a larger backlog and multiple feature teams all working on the same effort. In this article, ti&m's CTO Martin Fabini explores an organizational design based on LeSS, the framework for scaling agile software development to multiple teams.

find more information
How Does HCE Address the EMV Goals?

Not a day goes by without new mobile payment apps popping up or the Original Equipment Manufacturers, also called OEMs, launching their own mobile wallets (Apple Pay, Samsung Pay, Android Pay) in additional countries. Especially Switzerland plays an interesting role by focusing on the payment solution TWINT to solve the local mobile payment needs. However, regardless of the payment app and underlying technology, all solutions need to balance usability and security in order to justify a valid business case.

find more information