Lazy Load Angular Library (Micro-Applications)
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 workspacetsconf.json
file in thepaths
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.