7

As far as I know the call signature of canActivate looks like this:

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {

}

I have a service that expects the name of a component and returns the necessary user role to access this component, so that I can check in the canActivate function of my guard, whether the active user has the corresponding role or not. My problem is, that I don't know how to access the component. After some googling I found solutions like this:

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
  const nameOfComponent = route.routeConfig.component.name;
  return doSomeRoleCheck(nameOfComponent);
}

But in my case I just get an error: "Cannot read proprty 'name' of undefined". How can I access the component of the active Route or especially the name as a string?

Edit

I found out, that I do check this guard on a parent route. When I check it in the child route it works. How can I access the child component from parent can ActivatedRouteSnapshot

Stefan Falk
  • 23,898
  • 50
  • 191
  • 378
Michael L.
  • 273
  • 1
  • 3
  • 17
  • with your current setup, you could probably expose a static member on the component class and access that directly as a list of accepted roles. If you're doing it generically, it would be best to have an interface to ensure that all routing components have that list. – chrispy Dec 06 '17 at 17:16
  • Sounds interesting. Can you sketch up how this would be programmed? – Michael L. Dec 06 '17 at 17:17
  • sure, I'll put it below. – chrispy Dec 06 '17 at 17:33
  • Thanks. Now I understand what you mean. Another question: I have a parent route "" which guards all child routes. As the parent route has no related component, it throws "Cannot read property 'name' of undefined". How can I access the component of the child route? – Michael L. Dec 06 '17 at 17:46
  • You could put the guard on each child route, or if every child route has the same role requirements for that parent, put the guard and the roles on the parent and give it a simple component to hook into. – chrispy Dec 06 '17 at 17:49
  • yeah, that should work – Michael L. Dec 06 '17 at 17:50

1 Answers1

7

Something like this would probably work in your case:

export abstract class RoleCheck {
  myRoles: string[];
}

@Component({ ... })
export class YourComponent extends RoleCheck {
  public myRoles = ['User', 'Admin'];
}

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
  if (!route.routeConfig.component instanceof RoleCheck) {
    //Throw
  }
  return doSomeRoleCheck(route.routeConfig.component.myRoles)
}
chrispy
  • 3,552
  • 1
  • 11
  • 19
  • How would I make that work with an interface given that my class already extends from another class. (an not wanting to create yet more specific extensions) – Eraldo Jun 11 '20 at 15:10
  • You could export another version that imports your existing version and extends it while implementing `RoleCheck` as an interface, then just export that version as default, or something similar. – chrispy Jul 02 '20 at 00:08
  • `routeConfig.component` doesn't return a component instance, it doesn't seem like you should be able to access properties like this unless they were `static`? – MattTreichel Oct 02 '20 at 15:57
  • My comment was from 3 years ago, so the API may have changed. You are correct that if it's not a component instance the roles would need to be static, but I don't see why that wouldn't be fine here. – chrispy Oct 03 '20 at 17:10