Useful Development

Angular: Creating configurable libraries with angular cli

banner image
posted on Jun 21, 2019

When moving a module into a library the using the Angular cli environment settings is no longer an option. This post shows another option for supporting configuration settings in the module. There is already a common pattern for configuring a module using forRoot. With a few changes this can be used to for a library as well.

Getting started

There is a sample for the post available here. If you want to jump to the good bits just clone the repo and skip the rest of this section.

 
git clone https://github.com/JayChase/ng-configurable-libs-sample.git
npm i
 

To get started create a project, an app (web-app) and a library (normal-lib).

 
ng n ng-configurable-libs-sample --interactive=false --createApplication=false
cd ng-configurable-libs-sample
ng g application web-app --routing=true
ng g library normal-lib
 

Make the library configurable

For this sample we want normal-lib to have a setting which will allow the message displayed by the test component to be set by the consuming application. To do this follow the normal forRoot pattern.

Add the following files under the normal-lib/lib folder: normal-lib.config.ts

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

export const NORMAL_LIB_CONFIG = new InjectionToken(
  'NORMAL_LIB_CONFIG'
);
 
Export NormalLibConfig by adding it to public-api.ts.
 
export * from './lib/normal-lib-config';
export * from './lib/normal-lib.component';
export * from './lib/normal-lib.module';
export * from './lib/normal-lib.service';
 
Now we have the config and the injection token so we can provide it and everything in the library can inject the config and use it. The final part is to add the forRoot static function to the library module so the app can set the config when the library is imported. normal-lib.module.ts.
 
import { ModuleWithProviders, NgModule } from '@angular/core';
import { NormalLibConfig } from './normal-lib-config';
import { NORMAL_LIB_CONFIG } from './normal-lib-config.token';
import { NormalLibComponent } from './normal-lib.component';

@NgModule({
  declarations: [NormalLibComponent],
  imports: [
  ],
  exports: [NormalLibComponent]
})
export class NormalLibModule {
  static forRoot(normalLibConfig: NormalLibConfig): ModuleWithProviders {
    return {
      ngModule: NormalLibModule,
      providers: [
        {
          provide: NORMAL_LIB_CONFIG,
          useValue: normalLibConfig
        }
      ]
    };
  }
}
 

Use the config

In normal-lib.component.ts (the component created by default by ng g library) inject the config and use it.

 
import { Component, Inject } from '@angular/core';
import { NormalLibConfig } from './normal-lib-config';
import { NORMAL_LIB_CONFIG } from './normal-lib-config.token';

@Component({
  selector: 'lib-normal-lib',
  template: `
  <h2>Configurable message: {{ message }}</h2>
  `,
  styles: []
})
export class NormalLibComponent {
  message = this.normalLibConfig.message;

  constructor(@Inject(NORMAL_LIB_CONFIG) private normalLibConfig: NormalLibConfig) { }

}
 

Finally build the library.

 
ng build normal-lib
 

Using the library in the web-app

In the web-app environments files add a message property.

 
export const environment = {
  ...
  message: 'message'
};
 
Now import the NormalLibModule and call the forRoot function passing in the environment setting:
 
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { NormalLibModule } from 'normal-lib';
import { environment } from '../environments/environment';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    NormalLibModule.forRoot({
      message: environment.message
    })
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
 

Now the library module is imported and configured use the NormalLibComponent in the app to check everything is working. app.component.html

 
<div style="text-align:center">
  <h1>
    ng configurable libs sample
  </h1>
</div>
<lib-normal-lib></lib-normal-lib>
<router-outlet></router-outlet>
 

Now if you run the app the message value from environment.ts will be displayed.

 
ng s
 

Limitations

There is one common library type that this approach cannot be used for: lazy loaded feature modules. These require a different approach which will be covered in the next post.