Angular is a platform for building applications in TypeScript, HTML and CSS.

The basic building blocks of an Angular application are NgModules. An Angular app is typically comprised of a root module (that is responsible for bootstrapping) and one or more feature modules.

By default, NgModules are eagerly loaded, which means that as soon as the app loads, so do all the NgModules. However, Angular also provides support for lazy-loading NgModules. Lazy loading helps keep initial bundle sizes smaller, which in turn helps decrease load times.

Angular Libraries

Many applications need to solve similar problems so software engineer's like to craft generic solutions (for a particular domain) that can be re-used by other applications.

Angular Material (for example) is an Angular library that provides a comprehensive suite of UI components suitable for desktop, tablet and mobile devices.

Getting Started

I used the Angular CLI to generate the scafolding for each of my project's libraries, for example:

ng generate library sales

The CLI will create a new folder (/sales) in your workspace's /projects directory:

├── /serendipity
    └── /e2e
    └── /node_modules
    └── /projects
        └── /sales
    └── /src
        └── /app
            └── /core
        └── /assets
        └── /environments
        └── /themes
        ├── index.html
        ...
    ├── angular.json
    ├── package.json
    ├── tsconfig.json
    ...

Using your library

You don't have to publish your library in order to use it in your own app, but you do have to build it:

ng build sales

And, import from the library (in your core module) by name:

import { SalesModule } from 'sales';

Lazy loading your library

Let's start by creating a new folder (/lazy-loading) in our workspace's /src/app directory:

├── /serendipity
    └── /src
        └── /app
            └── /core
            └── /lazy-loading
                ├── sales-lib-wrapper.module.ts
            └── /shared
            ├── app.component.html
            ├── app.component.sccc
            ├── app.component.ts
            ├── app.module.ts
            ├── app-routing.module.ts
        └── /assets
        └── /environments
        └── /themes
        ├── index.html
        ...

Then all we need to do is create a simple wrapper module (sales-lib-wrapper.module.ts) for our library:

import { NgModule } from '@angular/core';

import { SalesModule } from 'sales';

@NgModule({
  imports: [ SalesModule ]
})
export class SalesLibWrapperModule {}

We also need to update our App routing module as follows:

...

const routes: Routes = [

  {
    path: 'sales',
    loadChildren: './lazy-loading/sales-lib-wrapper.module#SalesLibWrapperModule'
  }
  
  ...

];

@NgModule({
  imports: [
    RouterModule.forRoot(routes, {onSameUrlNavigation: 'reload'})
  ],
  exports: [
    RouterModule
  ]
})
export class AppRoutingModule {}

Now, that we are lazy loading the Sales module (in the App routing module), every route in the Sales library routing module is a child route:

...

const routes: Routes = [

  {
    // path: 'sales/contacts',
    path: 'contacts',
    component: ContactsComponent,
    canActivate: [AuthGuard],
    runGuardsAndResolvers: 'always'
  }
  
  ...
  
];

@NgModule({
  imports: [ RouterModule.forChild(routes) ],
  exports: [ RouterModule ]
 })
export class LazyLibRoutingModule {}

And we no longer need to (statically) import the SalesModule (in the core module):

// import { SalesModule } from 'sales';

Measurement

To help you visualise bundle sizes you can use the Webpack Bundle Analyzer.

For example ("target": "es5"):

ng build --prod --named-chunks --stats-json && ./node_modules/webpack-bundle-analyzer/lib/bin/analyzer.js ./dist/serendipity/stats.json

For example ("target": "es2015"):

ng build --prod --named-chunks --stats-json && ./node_modules/webpack-bundle-analyzer/lib/bin/analyzer.js ./dist/serendipity/stats-es2015.json

You can use Chrome DevTools Network panel to inspect the properties of individual resources:

Afterword

If you are lazy loading a library that relies on another libraries entryComponents then you need to ensure that its (entry component-related) services are providedIn the library (module), for example:

import { Injectable } from '@angular/core';
import { MatDialog, MatDialogRef, MatDialogConfig } from '@angular/material/dialog';
import { ComponentType } from '@angular/cdk/portal';

import { AlertDialogComponent } from '../../components/dialogs/alert-dialog/alert-dialog.component';
import { ConfirmDialogComponent } from '../../components/dialogs/confirm-dialog/confirm-dialog.component';

import { SerendipityComponentsModule } from '../../serendipity-components.module';

...

@Injectable({
  // providedIn: 'root'
  providedIn: SerendipityComponentsModule
})
export class DialogService {

  ...

}
Source Code:
References: