I have an application, which need to separate authenticated and guest users components. But I need, that both components will be loaded by '/' route. I wrote
{
path: 'desktop',
loadChildren: 'app/member/member.module#MemberModule',
canActivate: [LoggedInGuard],
},
{
path: '',
loadChildren: 'app/guest/guest.module#GuestModule',
canActivate: [GuestGuard],
},
And it works. But how to make, that both component load by same url?
I had tried to write path: ''
for Member's module route, but the second router rule is not performed.
Here are guards code:
LoggedInGuard:
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
if(this.sessionService.isLoggedIn()) {
return true;
} else {
return false;
}
}
GuestGuard:
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
if(!this.sessionService.isLoggedIn()) {
return true;
} else {
return false;
}
}
Here is a plunker: http://embed.plnkr.co/VaiibEVGE79QU8toWSg6/
How should I do it properly? Thank you
Angular Router supports multiple outlets in the same application. A component has one associated primary route and can have auxiliary routes. Auxiliary routes enable developers to navigate multiple routes at the same time.
What is the difference between [routerLink] and routerLink ? How should you use each one? They're the same directive. You use the first one to pass a dynamic value, and the second one to pass a static path as a string.
A Wildcard route has a path consisting of two asterisks (**). It matches every URL, the router will select this route if it can't match a route earlier in the configuration. A Wildcard Route can navigate to a custom component or can redirect to an existing 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With