Search code examples
angularangular-routing

Retrieving and reacting to route params in FactoryProvider


I want to use a FactoryProvider that gets chooses a service implementation based on current URI.

Furthermore, one of the implementations needs to react to Route Parameters (as defined by route/:parameter).

As shown below, I subscribe to changes of Unfortunately, I can't access the parameters that way. A component that is instantiated in the primary router outlet can access them, but the FactoryProvider can't.

Is there anything I might have missed?

module code (abbreviated for improved readability)

@NgModule({
  declarations: [],
  imports: [],
  exports: [],
  providers: [XServiceProvider],
  entryComponents: []})

Factory Provider

export const XServiceProvider = {
    provide: XService,
    useFactory: XServiceFactory,
    deps: [Http, HttpService, ConfigurationService, DatePipe, Router, ActivatedRoute]
  };

const XServiceFactory = (http: Http,
                               httpService: HttpService,
                               configuration: ConfigurationService,
                               datePipe: DatePipe,
                               router: Router,
                               activatedRoute: ActivatedRoute) => {

   const useAlternateImpplementation= router.url.startsWith('/mg/');


if (useAlternateImpplementation === true) {
    const service = new XyService(http, httpService, configuration, datePipe);

for (const child of activatedRoute.root.children) {
        if (child.outlet === PRIMARY_OUTLET) {
            child.url.subscribe(params => {
                 service.setParam(params[parameter]);
             });
        }
    }
    return service;
  }
  return new XService(http, httpService, configuration, datePipe);
};

Solution

  • Solved the issue by changing the dependency injection scope from Singleton to Transient.

    Here's how:

    • remove the provider definition from the module
    • add it to the components that actually require it

    Code sample:

    @NgModule({
        declarations: [],
        exports: [],
        providers: [ /* remove here! */ ]
    })
    //...
    
    @Component({
      selector: '...',
      templateUrl: '...',
      styleUrls: ['...'],
      providers: [XServiceProvider] /* add here */
    })
    //...
    

    Here's (probably) why:

    • Services that are declared as module-wide providers are created once per application creation. Thus, you cannot load the app, use service implementation one, change context, use implementation two.
    • Declaring the providers per component changes this pattern. The components are instantiated and destroyed, based on routing. That said, another route can now lead to another implementation.

    I was inspired by a blog post to try this.

    Still, I have no idea how to access route params beyond the scope of components that a rendered to the primary outlet.