Brian Noyes Brian Noyes - 9 months ago 47
TypeScript Question

Pass parameter into route guard

I'm working on an app that has a lot of roles that I need to use guards to block nav to parts of the app based on those roles. I realize I can create individual guard classes for each role, but would rather have one class that I could somehow pass a parameter into. In other words I would like to be able to do something similar to this:

{
path: 'super-user-stuff',
component: SuperUserStuffComponent,
canActivate: [RoleGuard.forRole('superUser')]
}


But since all you pass is the type name of your guard, can't think of a way to do that. Should I just bit the bullet and write the individual guard classes per role and shatter my illusion of elegance in having a single parameterized type instead?

Answer Source

Try this,

app/service-factories/role-guard.ts

import UserService from 'app/services/user.services';

export default function roleGuard(...roles: string[]) {

  @injected class RoleGuard {
    constructor(readonly userService: UserService) { }

    async canActivate() {
      // hypothetical example
      const userRoles = await userService.getCurrentUserRoles();
      return userRoles.some(role => roles.includes(role));
    }
  }

  return RoleGuard;

  function injected(t: {}) { return t; } // POD (plain old decorator)
}

And use it like this

import roleGuard from 'app/service-factories/role-guard';
....
{
  path: 'super-user-stuff', 
  component: SuperUserStuffComponent,
  canActivate: [roleGuard('superUser')]
}

each time the function is invoked, it creates a brand new guard class for you. And the implementation of the guard class is trivial because it has direct access to the roles parameter simply because of lexical scoping.