Angular4 Routing: Force Navigation By Bypassing Route Guard
Asked Answered
S

4

10

When my users have dirty changes on a screen, I have a route guard to prompt them to save or discard when they try to navigate away.

However, if I am logging them out due to inactivity, I want to force navigation and bypass the route guard (by discarding their changes), in order to ensure that their screen is blanked.

How can I bypass route guards?

Simoom answered 3/5, 2017 at 17:54 Comment(0)
R
11

My solution was as follows:

My logout method after successfully logging out, redirects the app to the /login route:

@Injectable
export class AuthService {
  // ...
  logout () {
    // do logout
    this.router.navigate(['/login']);
  }
}

My guard is then as follows:

@Injectable
export class DiscardChangesGuard implements CanDeactivate<MyComponent> {

  canDeactivate(component: MyComponent, currentRoute: ActivatedRouteSnapshot,
                currentState: RouterStateSnapshot, nextState?: RouterStateSnapshot): Observable<boolean> | boolean {

    if (nextState.url === '/login') {
      return true; // bypass checks if we are trying to go to /login
    }

    // perform regular checks here
  }
}
Rosariorosarium answered 5/10, 2017 at 11:20 Comment(0)
S
9

Here's how you can force a bypass on a per-navigation basis. In my case I was navigating back to the account dashboard after the user submits a form with account info.

Ideally you would check for unsaved changes in the form and bypass based on that, but my forms don't have that feature yet.

form component:

this.router.navigateByUrl('/', { state: { bypassFormGuard: true } })

router guard:

@Injectable()
export class ExitFormGuard implements CanDeactivate<ComponentCanDeactivate> {

   constructor(private router: Router) {}

   canDeactivate(component: ComponentCanDeactivate): boolean | Observable<boolean> {
      if (this.router.getCurrentNavigation()?.extras?.state?.bypassFormGuard) {
         return true
      }

      // perform normal checks
   }
}

Spouse answered 4/12, 2020 at 12:23 Comment(2)
Thanks that is a good idea, but it could be shorter by using the optional chaining with ?. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… if (navObject?.extras?.state?.bypassFormGuard) { return true; }Hereditable
@Hereditable Good point, I updated the answerSpouse
S
1

My goal was to bypass route guards without touching every route guard in the app, but I was unable to do that. Ultimately I created a new RouteBypassService to inject into every route guard. RouteBypassService implements shouldBypass, which returns true if the route guard should allow navigation for some overriding out of band reason, such as authorization. In my case shouldBypass returns true iff there is no user signed in. (After they sign out, we let them get off the screen no matter what).

It's not ideal because every author of a route guard has to affirmatively remember to add the bypass, but it's not terribly entangling.

Simoom answered 15/5, 2017 at 20:25 Comment(0)
P
0

I create something like a white list to write every route I want to bypass guard

white-list.ts

export const WhiteListedRoutes: string[] = [
  '/route1',
  '/route2',
];

Then in the guard something like:

import { WhiteListedRoutes } from './white-list';

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}

  canActivate(_next: ActivatedRouteSnapshot, _state: RouterStateSnapshot): boolean | Promise<boolean> {
    const route = state.url.split('?')[0];
    const queryParams = _route.queryParams;
    const isWhiteListed = WhiteListedRoutes.findIndex((r: string) => r === route) >= 0;

    return this.status || isWhiteListed || this.router.navigate(['/'], { state: { r: route }, queryParams } );
  }
}
Perea answered 3/1, 2022 at 23:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.