2

I have a navigation with three submenus and their according children routes. Now some of the submenus are not visible (ngIf), depending on the claims a user got from the server.

Wenn the main menu is clicked, I redirect to one of the submenus but sometimes this submenu is not accessible - then I would like to redirect to the next sibling:

{
    path: 'mymainmenue',
    children: [
        { path: 'submenu1', component: SubMenu1Component },
        { path: 'submenu2', component: SubMenu2Component },
        { path: 'submenu3', component: SubMenu3Component },
        { path: '', redirectTo: 'submenu1' },
    ]
}

Now the "sometimes" I'm able to calculate. I've tried to create three routes with an empty path ('') and a CanActivate-Guard. Similar to this:

{
    path: 'mymainmenue',
    children: [
        { path: 'submenu1', component: SubMenu1Component },
        { path: 'submenu2', component: SubMenu2Component },
        { path: 'submenu3', component: SubMenu3Component },
        { path: '', redirectTo: 'submenu1', canActivate: [ClaimsGuard] },
        { path: '', redirectTo: 'submenu2', canActivate: [ClaimsGuard] },
        { path: '', redirectTo: 'submenu3', canActivate: [ClaimsGuard] },
    ]
}

hoping I could use the route and my rules to return false in case for example the submenu1 is invisible and the router would chose the next possible route (redirect to submenu2).

The problem with my approach is that redirectTo gets executed even before my canActivate-method is called.

What would be the best way to do this?

NoRyb
  • 1,472
  • 1
  • 14
  • 35

1 Answers1

4

The reason your route guard is never called is that a route definition containing a redirect is assumed to be just a redirect in all cases. So Angular takes the redirect as whenever the user hits this path I want to immediately redirect. The route guard doesn't come into effect in this scenario.

To achieve what you are looking for you might want to move the redirect into your route guard. In addition to returning true and false a route guard can also redirect during the transition instead of cancelling it.

First, you'll want to remove the redirect in your route objects.

Next, the logic for your route guard

  1. Check whether the users can access this route
  2. If yes let user proceed to route
  3. If no redirect user to new route and return false afterwards

The redirect can be achieved by simply injecting Router into your route guard and calling this.router.navigate(['theNextSubmenuState']);.

An example of such a route guard could look like this:

@Injectable()
export class ClaimsGuard implements CanActivate {
  constructor(private router: Router, private yourClaimsService: YourClaimsService) {}

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    return this.checkClaimsAccess(state);
  }

  private checkClaimsAccess(state: RouterStateSnapshot) {
    if(this.yourClaimsService.userHasAccess(state) {
      return true;
    } else {
      const nextSubmenu = this.getNextSubmenuState(state);
      this.router.navigate([nextSubmenu])
      return false;
    }
  }

  private getNextSubmenuState(state: RouterStateSnapshot): string {
    //Some logic to figure out which submenu state you would like to go to next
  }
}

Angular also provides a pretty good example for how redirects in route guards work on their routing documentation - take a look at the 2nd code example of this section -> https://angular.io/guide/router#teach-authguard-to-authenticate

bbop99
  • 1,625
  • 1
  • 11
  • 25
  • How would I find the "next submenu" without using a big `if` and strings/constants in my guard? What concerns me, is that if I navigate inside the guard with my own logic, I don't need the router anymore - I could just set a wildcard route and handle everything myself. I was hoping the router had some better way of doing this like "if this one returns false, try the next one". – NoRyb Jun 14 '18 at 06:59
  • @NoRyb Hmm no don't think there's a built-in feature. But if you externalise your array of routes (just using TS) so you can import it into your route guard you can base all the logic off the initial route array and don't need large if statements. So the logic for finding the next route would be 1) find the current route in the array of routes (you'll get information about the current route passed into the guard) and then 2) go to the route one index on. – bbop99 Jun 14 '18 at 16:51