Search code examples
angularangular-libraryangular-dependency-injection

Making sure my Angular library's service is constructed offering standalone friendly provide function


I am authoring an Angular library. It contains a service, which runs initialization logic in the constructor that is required for the library to work properly. I have set up the library module with a static forRoot() method like the following:

@NgModule({
  imports: [CommonModule],
})
export class LibModule {
  static forRoot(
    config?: Partial<LibConfig>,
  ): ModuleWithProviders<LibModule> {
    return {
      ngModule: LibModule,
      providers: [{ provide: MY_LIB_CONFIG, useValue: config }],
    };
  }

  // This is necessary, so the service is constructed, even if the service is never injected
  constructor(private myLibService: MyLibService) {}
}

Note the constructor where the lib service is injected. Without the constructor, the service initialization logic would only run if the library consumer injects the service, which is not what I want. The service should always be constructed. This works fine for me.

Now I want to offer a more standalone friendly provideMyLib() function. Here is what I implemented so far:

export function provideMyLib(config?: Partial<LibConfig>): EnvironmentProviders {
  return makeEnvironmentProviders([
    { provide: MY_LIB_CONFIG, useValue: config },
  ]);
}

It works fine in general, but the same problem here, the service is only constructed when the consumer injects it. What I have tried to fix it:

  • Adding the service to the providers array -> service is not constructed
  • Using the inject() function inside the provideMyLib() function -> Error: inject must be run in injection context
  • Creating a custom injector and inject the service with it -> works, but my service needs to be singleton
  • Using variations of { provide: MyLibService, ... } -> service is not constructed

I have also researched implementations of various provide* functions in the Angular repository, didn't find anything. How can I implement my provide function?


Solution

  • If you're looking to init something, you need to hook it up to the ENVIRONMENT_INITIALIZER :

    export function provideMyLib(config?: Partial<LibConfig>): EnvironmentProviders {
      return makeEnvironmentProviders([
        { provide: MY_LIB_CONFIG, useValue: config }
        {
          provide: ENVIRONMENT_INITIALIZER,
          useValue: () => {
           inject(MyLibService)
           // do whatever you want here
          },
        }
      }
    }