angularangular-routingangular-routerangular-dependency-injection

Creating custom Router class in Angular leads to exception


I want to override the function navigate(commands: any[], extras?: NavigationExtras): Promise<boolean> of Router to always set queryParamsHandling to true in the NavigationExtras.

Therefore I implemented following class:

@Injectable({
    providedIn: 'root'
})
export class MmosRouter extends Router {

    constructor(
        rootComponentType: Type<any>,
        urlSerializer: UrlSerializer,
        contexts: ChildrenOutletContexts,
        location: Location_2,
        injector: Injector,
        compiler: Compiler,
        @Inject(ROUTES) config: Route[][]
    ) {
        super(rootComponentType, urlSerializer, contexts, location, injector, compiler, flatten(config));
    }


    navigate(commands: any[], extras?: NavigationExtras): Promise<boolean> {
        extras = { ...extras, queryParamsHandling: 'preserve' };
        return super.navigate(commands, extras);
    }
}

Because I want to inject the class through a components constructor I have to add the class to providers in app.module.ts.

@NgModule({
  declarations: [
    ...
  ],
  imports: [
    ...
  ],
  providers: [
    ...
  {
    provide: Router,
    useFactory: (rootComponentType: Type<any>, urlSerializer: UrlSerializer, contexts: ChildrenOutletContexts, location: Location_2, injector: Injector, config: Route[][]) => {
      return new MmosRouter(rootComponentType, urlSerializer, contexts, location, injector, config);
    },
    deps: [ApplicationRef, UrlSerializer, ChildrenOutletContexts, Location_2, Injector, Compiler, ROUTES]
  },
    ...
  ],
  bootstrap: [...],
  exports: [
    ...
  ],
})
export class AppModule {
  constructor(service: ApmService) {}
}

For my information, that should be everything to get this to work.

However, whenever I start the application, I get following NullInjectionError:

core.mjs:9171 ERROR NullInjectorError: R3InjectorError(AppModule)[MmosRouter -> Function -> Function -> Function]: 
NullInjectorError: No provider for Function!
  at NullInjector.get (core.mjs:8191:27)
  at R3Injector.get (core.mjs:8618:33)
  at R3Injector.get (core.mjs:8618:33)
  at R3Injector.get (core.mjs:8618:33)
  at injectInjectorOnly (core.mjs:4782:33)
  at Module.ɵɵinject (core.mjs:4786:12)
  at Object.MmosRouter_Factory [as factory] (mmosRouter.ts:17:24)
  at R3Injector.hydrate (core.mjs:8719:35)
  at R3Injector.get (core.mjs:8607:33)
  at ChainedInjector.get (core.mjs:13811:36)

(line 17, mentioned in the error, is the class signature of MmosRouting)


Solution

  • The problem was Type<any> in the constructor of MmosRouter.

    Because Type is a FunctionConstructor and that interface has some properties with type Function, I think angular had problems to provide them when I wanted to inject my custom router.

    So the Solution is removing the parameter rootComponentType: Type<any> from the constructor and pass AppComponent into the super(...) in the body. And then you also don't need them in the providers in app.module.ts.

    So consider changing:

    Custom Router:

    @Injectable({
      providedIn: 'root'
    })
    export class MmosRouter extends Router {
    
      constructor(
        //remove rootComponentType: Type<any>
        ...
      ) {
        super(AppComponent, ...);
      }
    
    
      navigate(commands: any[], extras?: NavigationExtras): Promise<boolean> {
        ...
      }
    }