Why the guard is not trigger when I route in Angular?

Viewed 572

I created lazy routes: one for the app shell module the other for auth module.

When I click on login then I redirect to shell app it's okay.

But when I click on logout and redirect to the main route (in order to trigger the guard) it's seems that guard don't invoke.

How do I know it? because the I don't see the console output after I click on logout.

it not even trigger the guard which is the problem. so I not redirect to login page.

Yes of course I can navigate directly to login route but I want to guard to do it for me.

So I create a simple app that demonstrate the problem in here and in codesandbox.io

import { BrowserModule } from "@angular/platform-browser";
import { Injectable, NgModule } from "@angular/core";
import { Component } from "@angular/core";
import {
  ActivatedRouteSnapshot,
  CanActivate,
  CanActivateChild,
  CanLoad,
  Router,
  RouterModule,
  RouterStateSnapshot,
  UrlTree
} from "@angular/router";
import { CommonModule } from "@angular/common";
import { Observable, of } from "rxjs";

let loggedIn = false; // <--- this is demo. it's set if the user login or not.

@Component({
  template: '<button (click)="login()">login</button>'
})
export class LoginComponent {
  constructor(private router: Router) {}

  login() {
    console.log("in login");

    loggedIn = true;
    this.router.navigate(["/"]);
  }
}

@NgModule({
  imports: [
    CommonModule,
    RouterModule.forChild([{ path: "", component: LoginComponent }])
  ],
  declarations: [LoginComponent]
})
export class AuthModule {}

///////////////////////////////

@Injectable()
export class IsLoggedInGuard implements CanActivate, CanActivateChild, CanLoad {
  constructor(private readonly router: Router) {}

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean | UrlTree> {
    return this.isAuthenticated(state.url);
  }

  canActivateChild(): Observable<boolean | UrlTree> {
    return this.isAuthenticated();
  }

  canLoad(): Observable<boolean | UrlTree> {
    return this.isAuthenticated();
  }

  private isAuthenticated(url?: string): Observable<boolean | UrlTree> | any {
    if (!loggedIn) {
      return this.router.createUrlTree(["/auth"], {
        queryParams: { url }
      });
    }
    return true;
  }
}
////

@Component({
  template: '<button (click)="logout()">logout</button>'
})
export class ShellComponent {
  constructor(private router: Router) {}

  logout() {
    console.log("in logout");

    loggedIn = false;
    this.router.navigate(["/"]);
  }
}

@NgModule({
  imports: [
    CommonModule,
    RouterModule.forChild([{ path: "", component: ShellComponent }])
  ],
  declarations: [ShellComponent]
})
export class ShellModule {}

@Component({
  selector: "app-root",
  template: `<router-outlet></router-outlet>`
})
export class AppComponent {
  title = "CodeSandbox";
}

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    RouterModule.forRoot([
      {
        path: "",
        loadChildren: () => Promise.resolve(ShellModule),
        canActivate: [IsLoggedInGuard]
      },
      { path: "auth", loadChildren: () => Promise.resolve(AuthModule) }
    ])
  ],
  providers: [IsLoggedInGuard],
  bootstrap: [AppComponent]
})
export class AppModule {}
2 Answers

You can't do that since you're already on the same route, canActivate "deciding if a route can be activated" or not, but in your case, it's already activated after you navigate to it on login.

Even changing the onSameUrlNavigation configuration to reload, or using RouteReuseStrategy to reload the component on navigate to the same route, even this won't work because this will reload the component only without triggering the canActivate guard again (since the route already activated).

It's better in your case to redirect the user to the login page on logout.

The problem is not with your code, it's the way the router works. Since you're navigating to the same route, the router will not reload the page, the component will not be loaded again, and the guard will not be invoked.

You can see this if you put an ngOnInit in the ShellComponent in your code. Even though you call router.navigate to the same route nothing happens and ngOnInit is not called.

You either have to navigate to another page (that has the same guard, that will then be invoked), or just navigate to login: /auth, which works. I don't see why not send the user to login when the logout button is pressed. Maybe you can solve this by changing the router configuration to reload the page when the same route is invoked, but that seems unnecessary, and will affect the whole app.

Related