Home > Software engineering >  Modify a singleton dependency on each module import
Modify a singleton dependency on each module import

Time:12-21

I have a use case in my application:

There is an ApiModule, there are 2 services in it, both are scoped to the module itself. One is ApiRouteService and the other one is ApiRouteLoaderService. Now for the purpose:

  1. ApiRouteService: It maintains a cache of api routes (route method, name, url etc) and returns an api route object identified by name when asked for it.
  2. ApiRouteLoaderService: It just loads the raw api routes and parses them to be used by ApiRouteService.

The modules in my application are lazy loaded. Now here's the scenario:

When the AuthModule is lazy loaded. It will import the ApiModule.forFeature(AuthApiRoutes). The forFeature method looks like:

@NgModule({
    imports: [CommonModule],
    providers: [ApiRouteService, ApiRouteLoaderService],
})
export class ApiModule {
    public static forFeature(apiRoutes: Array<IRawApiRoute>): ModuleWithProviders<ApiModule> {
        return {
            ngModule: ApiModule,
            providers: [
                {
                    provide: ApiRouteService,
                    useFactory: (apiRouteLoaderService: ApiRouteLoaderService, apiRouteService: ApiRouteService) => {
                        const parsedApiRoutes = apiRouteLoaderService.loadRoutes(apiRoutes);

                        // Now I need the singleton instance of ApiRouteService, so I can call
                        // apiRouteService.registerRoutes(parsedApiRoutes); and it will add the routes in
                        // apiRouteService instance along others.

                        // But I can't call the above, because I need the initial instance of ApiRouteService
                        // Also note that this function will be called only once when ApiModule is loaded
                        // in any module
                    },
                    deps: [ApiRouteLoaderService, ApiRouteService /* <-- This ApiRouteService causes circular loop */],
                },
            ],
        };
    }
}

How I can achieve this? Is there a better way to do it?

CodePudding user response:

Maybe I am misunderstanding what you want to do, but it looks like you just need to call apiRouteLoaderService.loadRoutes and apiRouteService.registerRoutes on module initialization. I don't think you actually want to provide another instance of the ApiRouteService do you? If that is the case I would just provide the apiRoutes, inject the dependencies and call the methods like this:

export const ROUTES = new InjectionToken<Array<IRawApiRoute>>('routes');

@NgModule({
    imports: [CommonModule],
    providers: [ApiRouteService, ApiRouteLoaderService],
})
export class ApiModule {
    constructor(
        private apiRouteService: ApiRouteService,
        private apiRouteLoaderService: ApiRouteLoaderService,
        @Inject(ROUTES) @Self() @Optional() private routes?: Array<IRawApiRoute>
    ) {
        if(routes) {
            const parsedApiRoutes = this.apiRouteLoaderService.loadRoutes(routes);
            this.apiRouteService.registerRoutes(parsedApiRoutes);
        }
    }

    public static forFeature(apiRoutes: Array<IRawApiRoute>): ModuleWithProviders<ApiModule> {
        return {
            ngModule: ApiModule,
            providers: [{
                provide: ROUTES,
                useValue: apiRoutes,
            }],
        };
    }
}
  • Related