5
votes

I have an ErrorModule (eager) configured as follows:

export const CONFIG = new InjectionToken<ErrorConfig[]>('Module errors configuration.');


@NgModule({
  imports: [... ]
})
export class ErrorModule {
  static forRoot(config: ErrorConfig): ModuleWithProviders {
    return {
      ngModule: ErrorModule,
      providers: [
        ErrorService,
        { provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },
        { provide: CONFIG, useValue: config, multi: true }
      ]
    };
  }

  static forChild(config: ErrorConfig): ModuleWithProviders {
    return {
      ngModule: ErrorModule,
      providers: [
        { provide: CONFIG, useValue: config, multi: true }
      ]
    };
  }
}

Core module then imports ErrorModule as follows:

@NgModule({
  imports: [
    ErrorModule.forRoot(ERROR_CONFIG)
  ], ...

Lazy loaded child feature module:

@NgModule({
  imports: [
    ErrorModule.forChild(ERROR_CONFIG_CHILD)
  ], ...

I would like to see both configurations ERROR_CONFIG and ERROR_CONFIG_CHILD injected into ErrorService defined in ErrorModule:

@Injectable
export class ErrorService {
    constructor(@Inject(CONFIG) private readonly errorConfigs: ErrorConfig[])
}

But the service's errorConfigs (in constructor) contains only CONFIG defined in core.module - the one defined in forRoot() function (An array with one element).

Lazy feature module has been loaded and initialized and the ErrorModule.forChild(...) invoked

It kind of makes sense that the ErrorService has been constructed in time when only core module's CONFIG injection token was available - the feature module has not beeen loaded yet.

I have also tried to import the CONFIG using Angular Injector outside of constructor (method in ErrorService) and the result was same.

const configs: any[] = injector.get(CONFIG); // returns only root CONFIG

So my question is: Is it possible to somehow access a provider provided by a lazy module in app module? (Access it in app's root injector?)

1
Did you find an alternative? I'd like to implement basically the same thing, pluggable features. This does not seem to be feasible through DI at a first glance, and I'm not really surprised nor deceived. But it's too bad that this gonna end up with a singleton service you can register to (in a non-declarative or IoC way). - Morgan Touverey Quilling
@MorganTouvereyQuilling see my answer bellow - an alterative. - Felix
@Felix @MorganTouvereyQuilling Do you guys think we should implement this use case like in @angular/router module? I still haven't unraveled the "black magic" done in the router config loader though. 🤷 • router module • router config loader - Glenn Mohammad
Did you ever find an 'angular-way' solution for this? I basically need the same thing - devqon
@devqon I used the solution below. I have no angular-way solution. - Felix

1 Answers

2
votes

I ended up with a solution, that uses ErrorService defined in CoreModule to register configurations when FeatureModule is initialized:

import { ErrorService } from '@core/error.service';

@NgModule({
    imports: [ CommonModule, ForFeatureModule, TranslateModule.forChild() ],
    declarations: [FeatureModule],
    providers: []
})
export class FeatureModule {
    constructor(
        private readonly translate: TranslateService, private readonly errorService: ErrorService) {
        translate.setTranslation('cz', i18n, true);

        // --- HERE --- errorService configures global error configuration
        errorService.addErrorMappings('FEATURE1', ERROR_MAPPING);
    }
}