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

121 Views Asked by At

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?

1

There are 1 best solutions below

3
Matthieu Riegler On BEST ANSWER

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
      },
    }
  }
}