Angular 5: Conditionally Set Default Route

2020-07-29 23:54发布

问题:

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?

回答1:

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