Useful Development

Angular: configurable lazy loaded libraries

banner image
posted on Feb 16, 2020

Using forRoot to pass configuration options into a library is a well used pattern in Angular.

 
  TestLibModule.forRoot({
      message: environment.message
    })
 

If the library is lazy loaded though there is no opportunity to do this as the library will now be dynamically imported by the router.

 
const routes: Routes = [
  {
    path: 'test',
    loadChildren: () => import('./test/test.module').then(m => m.TestModule)
  }
];
 

There is a way (hack?) round this which I'll show below. If you want to skip to the code then it's here.

Set up the project

To get started create a project, an app (web-app) and the lazy loaded feature library (test-lib).

 
ng n ng-configurable-lazy-lib-sample --interactive=false --createApplication=false --routing=true
cd ng-configurable-lazy-lib-sample
ng g application web-app --routing=true --style=scss
ng g library test-lib
 

Make the library configurable

For this sample we want test-lib to have a setting, which will allow the message displayed by the test component to be set by the consuming application.

Add the following files under the test-lib/lib folder.

test-lib.config.ts
 
export interface TestlLibConfig {
  message: string;
}
 
test-lib.config.token.ts
 
import { InjectionToken } from '@angular/core';
import { TestLibConfig } from './test-lib-config';

export const TEST_LIB_CONFIG = new InjectionToken(
  'TEST_LIB_CONFIG'
);
 

This is the new part. Instead of using forRoot add a static settings property to the module along with a factory to provide the TEST_LIB_CONFIG, returning the settings.

 
export function SettingFactory() {
  return TestLibModule.settings;
}

// @dynamic
@NgModule({
  declarations: [TestComponent],
  imports: [CommonModule, TestLibRoutingModule],
  providers: [
    {
      provide: TEST_LIB_CONFIG,
      useFactory: SettingFactory
    }
  ]
})
export class TestLibModule {
  static settings: TestLibConfig;

  constructor() {}
}
 

Use a configuration property in a component

Import the config into TestLibComponent and display the message property.

 
@Component({
  selector: 'lib-test-lib',
  template: `
    <h2>Configurable message: {{ message }}</h2>
  `,
  styles: []
})
export class TestLibComponent implements OnInit {
  message: string;
  constructor(@Inject(TEST_LIB_CONFIG) private testLibConfig: TestLibConfig) {}

  ngOnInit() {
    this.message = this.testLibConfig.message;
  }
}
 

Add routing to the library and update the library api

Add a file test-lib-routing.module.ts under the lib folder as below.

 
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { TestLibComponent } from './test-lib.component';

const routes: Routes = [
  { path: 'test', component: TestLibComponent },
  {
    path: '**',
    redirectTo: 'test'
  }
];

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

Export all the new files by adding them to public-api.ts.

 
export * from './lib/test-lib-config';
export * from './lib/test-lib-config.token';
export * from './lib/test-lib-routing.module';
export * from './lib/test-lib.component';
export * from './lib/test-lib.module';
export * from './lib/test-lib.service';
 

Lazy load the library in the app

Set up the route to the test-lib in app-routing.module.ts and set the library settings property.

 
const routes: Routes = [
  {
    path: 'test-lib',
    loadChildren: () =>
      import('test-lib').then(m => {
        m.TestLibModule.settings = {
          message: environment.message
        };

        return m.TestLibModule;
      })
  },
  {
    path: '**',
    redirectTo: 'test-lib'
  }
];
 

Replace the boilerplate app.component markup.

 
<h1>configurable lazy lib</h1>
<router-outlet></router-outlet>
 

Test it out

Build the library and the app. The run the app

 
ng build test-lib
ng build web-app
ng s
 

Now run it and if everything has gone to plan you will see that that message value set in the app is displayed.