Angular 2 different components with same route

So i was finally able to do this. The thing is Angular uses first match policy, so we need to match routes in a guard-type way, to be sure that right route with right module will be matched.

First thing we need to add custom matchers for our routes which will only match them on conditions that we want (user type for example).

{
 path: 'samePath',
 matcher: firstMatcher,
 loadChildren: '../first/first.module#FirstModule'
},
{
 path: 'samePath',
 matcher: secondMatcher,
 loadChildren: '../second/second.module#SecondModule'
}

And matchers code is something like this: In here i injected AuthService service from AppModule, and checked users type with it. So routes can be matched according to users type.

import { applicationInjector } from '../../main';

export function firstMatcher (url: UrlSegment[]) {
  const auth =  applicationInjector.get(AuthService);
  return auth.isUserType('admin') ? ({consumed: [url[0]]}) : null;
}

And now only thing we need is to create applicationInjector in our main module, so we could inject service in our matcher-function;

export let applicationInjector: Injector;

platformBrowserDynamic().bootstrapModule(AppModule).then((componentRef) => {
  applicationInjector = componentRef.injector;
})

You can use a Module that handles what module should be load by providing the ROUTES of the RouterModule using the useFactory provider of Angular.

The code could be something like that.

// HandlerModule

@NgModule({
  declarations: [],
  imports: [
    CommonModule,
    RouterModule
  ],
  providers: [
    {
      provide: ROUTES,
      useFactory: configHandlerRoutes,
      deps: [SessionService],
      multi: true
    }
  ]
})


export class HandlerModule {}

export function configHandlerRoutes(sessionService: SessionService) {
  let routes: Routes = [];
  if (sessionService.isLoggedIn()) {
    routes = [
      {
        path: '', loadChildren: () => import('app/member/member.module').then(mod => mod.MemberModule)
      }
    ];
  } else {
    routes = [
      {
        path: '', loadChildren: () => import(app/guest/guest.module).then(mod => mod.GuestModule)
      }
    ];
  }
  return routes;
}

Then in your AppRoutingModule the module of the path '' is going to be the HandlerModule:

// AppRoutingModule

 {
    path: '',
    loadChildren: () => import('app/handler/handler.module').then(mod => mod.HandlerModule)
}

After in the SessionService you have to update the Router.config when the value that provides the method isLoggedIn changes, because the application will only load the page (module) that had loaded the first time. This is because the function “configHandlerRoutes” use by the useFactory provider in the HandlerModule is only executed the first time we navigate to the “” path and after that the Angular Router already know which module he has to load.

In conclusion in the SessionService you have to do:

  export class SessionService {
  private loggedIn: boolean;
  constructor(private router: Router) {
    this.loggedIn = false;
  }

  public isLoggedIn(): boolean {
    return this.loggedIn;
  }

  public setLoggedIn(value: boolean): void {
    const previous = this.loggedIn;
    this.loggedIn = value;
    if (previous === this.loggedIn) {
      return;
    }
    const i = this.router.config.findIndex(x => x.path === '');
    this.router.config.splice(i, 1);
    this.router.config.push(
      {path: '', loadChildren: () => import('app/handler/handler.module').then(mod => mod.HandlerModule)}
    );
  }
}

That's it.

If you want another reference here is an article where they use the same approach: https://medium.com/@german.quinteros/angular-use-the-same-route-path-for-different-modules-or-components-11db75cac455