I am trying to find a way to conditionally render components in the router-outlet
of a child route, based on the user's role.
For example, I have a DashboardComponent, which contains a router-outlet
. I want the component that is rendered in child router-outlet
to be different dependent on the user role, which is passed in by the token, without having to specify additional routes.
I am hoping to try and write my Routes somewhere close to this:
{
path: 'dashboard',
component: DashboardComponent,
children: [
{ path: '', component: DataViewsComponent }, // For standard user role
{ path: '', component: AdminViewsComponent } // For the admin user role
]
}
This exact pattern doesn't work of course, because the router just sees two identical route paths.
I have managed to write a service that uses the canActivate
property to check the user's role and forward on to a different route dependant on the role:
@Injectable()
export class RoleRouteRedirectService implements CanActivate {
roleRoutingPermissions: any = {
// Will contain redirects for every route in the app
// For this example, only contains reroutes for the dashboard route
['dashboard']: [
{ roles: [ 'user' ], redirect: '/dashboard/admin' },
{ roles: [ 'admin' ], redirect: '/dashboard/admin' }
]
};
constructor(private router: Router) {}
canActivate(route: ActivatedRouteSnapshot): boolean {
// In the larger app, the roles are taken out of the token
// Here I've mocked them from localStorage for ease
const userRoles: string[] = localStorage.getItem('user-role').split(',');
const url: string = route.data.url;
// Find the object property matching the desired origin URL
if (url in this.roleRoutingPermissions) {
// Iterate over the array of route permissions in this property
for (const item of roleRoutingPermissions[url]) {
// If we find the entry for the role, apply the redirect
if (item.roles.some(role => userRoles.includes(role))) {
this.router.navigate([ item.redirect ]);
return false;
}
}
}
// Otherwise, fail
console.log('Could not find matching route permissions');
return false;
}
}
This would go with a different Routes pattern:
{
path: 'dashboard',
component: DashboardComponent,
canActivate: [ RoleRouteRedirectService ],
data: {
url: 'dashboard'
},
// Conditional Routes
children: [
{
path: 'user',
component: DataViewsComponent,
},
{
path: 'admin',
component: AdminHomeComponent,
}
]
}
Now, this works really well. However I have the following issues with this pattern:
- I wanted to avoid having additional routes - i.e. I don't want
dashboard/user
, I want justdashboard
that will render a different child for different users - I can see that this will quickly lead to my routes, and the RoleRouteRedirectService becoming incredibly convoluted as the app grows
- This doesn't feel like a correct way to be using a route guard (correct me if I am wrong!)
- I feel that there must be a way to handle this from within the router that I am missing
If anyone has any thoughts, I would love to hear them. I appreciate that this is a relatively convoluted problem - if I have missed out any information that would be useful, please comment and I will see what can be added in.