Search code examples
angularangular-routing

How to set child module route data?


I'm trying to set an array with the required roles to access a route. If route roles match user roles, access is granted. This is done by an AuthGuard.

This is how my routing module is configured:

const ROUTES: Routes = [
    {
        path: "",
        component: FullLayoutComponent,
        canActivate: [AuthGuard],
        children: [
            {
                path: "",
                redirectTo: "dashboard",
                pathMatch: "full",
            },
            {
                path: "dashboard",
                loadChildren: "./dashboard/dashboard.module#DashboardModule",
            },
            {
                path: "admin",
                loadChildren: "./admin/admin.module#AdminModule",
                data: { roles: [Role.Admin] }
            }
        ]
    },
    {
        path: "",
        component: NoLayoutComponent,
        children: [
            {
                path: "auth",
                loadChildren: "./auth/auth.module#AuthModule"
            },
            {
                path: "**",
                redirectTo: "/404"
            }
        ]
    }
];

And this is the AuthGuard:

import { Injectable } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { AuthService } from './auth.service';

@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {

    constructor(private router: Router, private authenticationService: AuthService) {

    }

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        const currentUser = this.authenticationService.user;

        if (currentUser) {

            // check if route is restricted by role
            console.log("route.data.roles", route.data.roles); // undefined
            console.log("route.parent.data.roles", route.parent.data.roles); // undefined
            console.log("user.roles", this.authenticationService.user.roles); // ok

            if (route.data.roles && !this.authenticationService.userHasRole(route.data.roles)) {
                // role not authorised so redirect to home page
                this.router.navigate(['/']);
                return false;
            }

            return true;
        }

        // not logged in so redirect to login page with the return url
        this.router.navigate(['/auth/login'], { queryParams: { returnUrl: state.url } });
        return false;
    }
}

Route data always print undefined.

I'm guessing something is wrong with the nested structure of the routes array, because if I set data in the first route, the one that is handled by FullLayoutComponent, it works fine. But that doesn't do the job because I need to be able to specify different roles to different children.

I tried a couple of variations like, setting the data property in the routes inside AdminModule for example, but with no luck. It's always undefined.


Solution

  • I'm answering myself here in case that help somebody else too.

    The solution that worked for me was to get rid of the FullLayoutComponent and NoLayoutComponent and then just handle the routes normally either with a component or a module.

    const ROUTES: Routes = [
        {
            path: "",
            redirectTo: "dashboard",
            pathMatch: "full",
        },
        {
            path: "dashboard",
            component: DashboardComponent,
            canActivate: [AuthGuard]
        },
        {
            path: "admin",
            loadChildren: "./admin/admin.module#AdminModule",
            canActivate: [AuthGuard],
            data: { roles: [Role.Admin] }
        },
        {
            path: "auth",
            loadChildren: "./auth/auth.module#AuthModule"
        },
        {
            path: "404",
            loadChildren: "./pages/four-zero-four/four-zero-four.module#FourZeroFourModule"
        },
        {
            path: "**",
            redirectTo: "/404"
        }
    ];
    

    With that route configuration, the AuthGuard works as expected and the only change I had to make was to adapt the layout to hide the sidebar and header when the user isn't logged in.