In my Angular(v7) application, clicking on a router link does not load the relevant component although the URL is changed in the browsers address bar.
Once the page is reloaded clicking on the refresh button of the browser then the router links start to work as expected. I have confirmed the behaviour in both Google Chrome and IE.
Could you please show me what I'm doing wrong and where? I have shared the relevant parts of my code below.
App module & router setup
import {
RouterModule,
Routes
} from "@angular/router";
...
const appRoutes: Routes = [{
path: 'login',
component: LoginComponent
},
{
path: 'bookings',
component: BookingsComponent,
canActivate: [AuthGuard]},
{
path: 'rates',
component: RatesComponent,
canActivate: [AuthGuard]},
{
path: '',
redirectTo: '/bookings',
pathMatch: 'full',
canActivate: [AuthGuard]
},
{
path: '**',
component: PageNotFoundComponent
},
];
@NgModule({
imports: [
RouterModule.forRoot(appRoutes, {
enableTracing: false
}),
BrowserModule,
...
]
})
export class AppModule {}
Router Links in HTML template
<a class="side-nav-link" routerLink="/rates" routerLinkActive="active">Rates</a>
<a class="side-nav-link" routerLink="/bookings" routerLinkActive="active">Bookings</a>
Auth Guard, If it is relevant
@Injectable({
providedIn: "root"
})
export class AuthGuard implements CanActivate {
constructor(private router: Router, private authService:AuthService) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
if (this.authService.loggedIn) {
return true;
}
this.router.navigate( ['/login'], { queryParams: { returnUrl: state.url }});
return false;
}
}
I had the same problem. It was caused by having two router-outlets on a single page and switching between the two using an ngIf. This seems to be an Angular anti-pattern.
Cause
Here is an example of what I was doing:
File: app.component.html
<!-- Show toolbar and sidenav if logged in -->
<mat-toolbar *ngIf="isLoggedIn()">My App</mat-toolbar>
<mat-sidenav-container *ngIf="isLoggedIn()">
<mat-sidenav>
<mat-nav-list>
<a mat-list-item routerLink="/page1">Page 1</a>
<a mat-list-item routerLink="/page2">Page 2</a>
</mat-nav-list>
</mat-sidenav>
<mat-sidenav-content>
<router-outlet></router-outlet>
</mat-sidenav-content>
</mat-sidenav-container>
<!-- Hide toolbar and sidenav if NOT logged in -->
<router-outlet *ngIf="isLoggedIn() === false"></router-outlet>
File: app-routing.module.ts
const routes: Routes = [
{ path: 'login', component: LoginComponent },
{ path: 'page-1', component: Page1Component, canActivate: [AuthGuard] },
{ path: 'page-2', component: Page2Component, canActivate: [AuthGuard] },
{ path: '**', redirectTo: 'page-1' }
];
I did this because I wanted two layouts: One for my main app content (with a toolbar and side-nav) and another for my login page (no toolbar and side-nav).
Solution
The correct way to do this is to create a two components, one for each of the layouts I wanted, and then use the routers children property. Link to docs. For example:
File: app.component.html
<router-outlet></router-outlet>
File: layouts/main-layout.component.html
<mat-toolbar>My App</mat-toolbar>
<mat-sidenav-container>
<mat-sidenav>
<mat-nav-list>
<a mat-list-item routerLink="/page1">Page 1</a>
<a mat-list-item routerLink="/page2">Page 2</a>
</mat-nav-list>
</mat-sidenav>
<mat-sidenav-content>
<router-outlet></router-outlet>
</mat-sidenav-content>
</mat-sidenav-container>
File: layouts/login-layout.component.html
<router-outlet></router-outlet>
File: app-routing.module.ts
const routes: Routes = [
{
path: 'login', component: LoginLayoutComponent, children: [
{ path: '', component: LoginComponent },
]
},
{
path: '', component: MainLayoutComponent, children: [
{ path: 'page-1', component: Page1Component, canActivate: [AuthGuard] },
{ path: 'page-2', component: Page2Component, canActivate: [AuthGuard] },
{ path: '**', redirectTo: 'page-1' }
]
},
];
Hope this helps :)
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