Lazy Load Angular Library (Micro-Applications)

Matt Vaughn
Sep 24, 2019 · 5 min read

Have you ever wanted to compose your large application with much smaller micro-apps — and lazy-load them as feature modules in your host application?

Angular 6 gave us the Angular Workspace and the new library project type. The library project type allows us to create things that can be shared — reusable code is good. No more copy and paste programming, right? There are many use cases for Angular libraries. Most of the common libraries include the following:

  • utilities
  • frameworks
  • components
  • foundational
  • cross-cutting concerns

These are single libraries that are used by multiple application and/or other library projects. However, there is a library type that I’m not sure how to categorize. We have these larger features in our existing applications that will be shared by at least 2 different application projects. They are small or micro-applications that are not entirely domain-specific.

Therefore, it makes sense to implement them as library projects. Great, now I would like to lazy-load this library as I would a feature module in my application. Is this even possible? I also want to take advantage of Nrwl’s Nx Workspace and the new import syntax for lazy-loading modules in Angular 8.

  • Nx Workspace
  • Angular 8 import modules.

Create a new Workspace

Let’s get started by creating a new Nx workspace to work in. I want to have the application and library projects in the same environment. I do not have a need to publish the library projects to a package repository.

yarn create nx-workspace lazyLoadLibraryApp --npm-scope=lazy

Installing @nrwl/cli globally: yarn add @nrwl/cli

yarn global add @nrwl/cli

Enable Angular (CLI) Templates

Install the @nrwl/angular package.

yarn add @nrwl/angular

Update the cli setting in the angular.json file.

"cli": { "defaultCollection": "@nrwl/angular" }


Add Host Application

Now that the application template is available from the @nrwl/angular package, create a new application project. The host application project will be the host for the library micro-app.

ng g application appHost
? Which stylesheet format would you like to use? SASS(.scss) [ http://sass-lang.com ]
? Would you like to configure routing for this application? Yes

Add Library Project for Micro-App

This library project will be implemented as a micro-application — it will require a host application project. Typically, you wouldn’t think of a library project as an application — however, in this case, it can be a self-contained application that has:

  • services
  • components
  • pipes
  • directives
  • classes
ng g library securityApp --publishable
? Which stylesheet format would you like to use? SASS(.scss) [ http://sass-lang.com ]

This application library doesn’t even have to export any of its items. It is a self-contained application. By default the index.ts barrel file exports all that is needed (the module) to lazy-load the micro-application:

libs\security-app\src\index.ts

export * from './lib/security-app.module';

Routing Modules

Create a routing module in the app-host project to configure the routes for lazy-loading.

ng g module appRouting --project=app-host
CREATE apps/app-host/src/app/app-routing/app-routing.module.ts (196 bytes)

Create a const for the routes. This is where the magic happens. We will lazy-load the library micro-app here.

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{
// lazy-load the library application here;
}
];
@NgModule({
declarations: [],
imports: [
CommonModule,
RouterModule.forRoot(routes)
]
})
export class AppRoutingModule { }

Security-App Library Project

Before we can lazy-load the micro-app, we will need to implement some displayable elements in the micro-app. Create a routing module for the security library with a default route for the application.

ng g module appRouting --project=security-app
CREATE libs/security-app/src/lib/app-routing/app-routing.module.ts (196 bytes)

The AppRoutingModule contains a default route to a component.

import { NgModule, Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule, Routes } from '@angular/router';
import { LoginComponent } from '../login/login.component';

const routes: Routes = [
{
path: '', // default route for the security application.
component: LoginComponent
}
];

@NgModule({
declarations: [],
imports: [
CommonModule,
RouterModule.forChild(routes)
]
})
export class AppRoutingModule { }

Add a new component to the library. This is just to have at least an entry point for the new micro-app. The entry component could be a dashboard or any other type of container component. We only need a single/simple component to demonstrate lazy-loading a micro-app into the host application project.

ng g component login --project=security-app

Make sure that the SecurityAppModule declares the new component.

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AppRoutingModule } from './app-routing/app-routing.module';
import { LoginComponent } from './login/login.component';

@NgModule({
declarations: [
LoginComponent
],
imports: [
AppRoutingModule,
CommonModule]
})
export class SecurityAppModule {}

Lazy-Load the Library (micro-app)

We will now implement our lazy-loaded route in the application host project. Use the new import syntax.

We import the module that is located in the @lazy/security-app library project (as if it were a package courtesy of Nx --npm-scope @lazy). When I created the workspace, I used the --npm-scope option to create the scope lazy.

{
// lazy-load the library application here;
path: '',
loadChildren: () => import(`@lazy/security-app`).then(m => m.SecurityAppModule),
}

Notice that we do not need to import the SecurityAppModule - the import process is enabled using the import method in loadChildren.

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule, Routes } from '@angular/router';

// DO NOT IMPORT - IMPORTED VIA THE LAZY-LOAD PROCESS BELOW!!!!
// import { SecurityAppModule } from '@lazy/security-app'

const routes: Routes = [
{
// lazy-load the library application here;
path: '',
loadChildren: () => import(`@lazy/security-app`).then(m => m.SecurityAppModule),
}
];

@NgModule({
declarations: [],
imports: [
CommonModule,
RouterModule.forRoot(routes)
]
})
export class AppRoutingModule { }

Note: The import reference to @lazy/security-app is enabled through the configuration in the workspace tsconf.json file in the paths node.

"paths": {
"@lazy/security-app": ["libs/security-app/src/index.ts"]
}

Load It up Lazily

We are now ready to lazy-load the library project as a micro-app. I have been calling this a feature library recently. However, I do see some use cases where these could be smaller well-contained applications that are used to compose a larger application. It allows you to encapsulate the implementation in a single project type.

Serve it up — it works!

ng serve app-host

Please check out the code sample in the Workspace on GitHub.

I published a new book on Angular Architecture. Learn how to take advantage of library projects to create better architectures by organizing and reusing your code. Available on Leanpub.com.

Matt Vaughn

Written by

I love tacos, code, jazz, my husky and maybe 3 people in this world...and of course: Angular. Angularlicious podcast — more info at http://angularlicio.us

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade