Reputation: 4105
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:
dashboard/user
, I want just dashboard
that will render a different child for different usersIf 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.
Upvotes: 4
Views: 7976
Reputation: 36
Angular router uses first-match-wins
strategy for defined routes which combined with canActivate
is IMO a viable strategy for your use case. I don't think there's anything wrong with that approach.
Here is how I implemented my routing:
const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'logout', component: HomeComponent },
{ path: '', component: AuthorizedComponent, canActivate: [AuthGuard], children: [
{ path: 'onboard', component: OnboardComponent, canActivate: [AuthGuard] },
{ path: 'onboard_requests', component: OnboardRequestsComponent, canActivate: [AuthGuard] },
{ path: 'employee_list', component: EmployeeListComponent, canActivate: [AuthGuard] },
{ path: 'employee_detail', component: EmployeeDetailComponent, canActivate: [AuthGuard] }
]
}]
Although your use case seems slightly different than mine but as you can see I used two components HomeComponent
and AuthorizedComponent
based on canActivate
for root path
Upvotes: 0
Reputation: 286
An alternative would be to create a parent component which holds both, the DataViewsComponent
and the AdminHomeComponent
. Based on the user´s role, the relevant component can be chosen via an ngIf
. Then you can move the logic from your RoleRouteRedirectService
in this parent component:
<DataViewsComponent *ngIf="user.hasRole('user')"></DataViewsComponent>
<AdminHomeComponent *ngIf="user.hasRole('admin')"></DataViewsComponent>
Upvotes: 5