0

I'm trying to use the canLoad function with routes, but it doesn't seem to work.

I don't know why, maybe you can't use it with canActivate or something, but since I don't know, I thought someone would here.

The code runs, when serving with aot compilation I get this :

chunk {admin.module} admin.module.chunk.js, admin.module.chunk.js.map () 28 kB {main} {pilotage.module} {suiviprod.module}
chunk {inline} inline.bundle.js, inline.bundle.js.map (inline) 5.83 kB [entry]
chunk {main} main.bundle.js, main.bundle.js.map (main) 3.5 MB {vendor} [initial]
chunk {pilotage.module} pilotage.module.chunk.js, pilotage.module.chunk.js.map () 17.2 kB {admin.module} {main} {suiviprod.module}
chunk {styles} styles.bundle.js, styles.bundle.js.map (styles) 267 kB {inline} [initial]
chunk {suiviprod.module} suiviprod.module.chunk.js, suiviprod.module.chunk.js.map () 20.4 kB {admin.module} {main} {pilotage.module}
chunk {vendor} vendor.bundle.js, vendor.bundle.js.map (vendor) 5.52 MB [initial]

But when I go to the modules, the console log isn't done.

Here is the code of my routes :

logged.module (main routing, see it as app.module)

export const loggedRoutes: Routes = [
  {
    path: 'logged', component: LoggedComponent, canActivate: [AuthGuardService], canActivateChild: [AuthGuardService], children: [
      { path: 'profile', component: ProfileComponent, children: [] },
      ...adminRoutes,
      ...mainRoutes,
      ...pilotageRoutes,
      ...suiviProdRoutes,
      { path: 'admin', loadChildren: 'app/logged/admin/admin.module#AdminModule', canLoad: [AdminGuardService] },
      { path: 'pilotage', loadChildren: 'app/logged/pilotage/pilotage.module#PilotageModule', canLoad: [AdminGuardService] },
      { path: 'suiviprod', loadChildren: 'app/logged/suiviprod/suiviprod.module#SuiviprodModule', canLoad: [AdminGuardService] },
      { path: '', redirectTo: '/logged/main/error', pathMatch: 'prefix' }
    ]
  },
];

admin.module (suiviprod and pilotage are the same, just with different routes & components)

export const adminRoutes: Routes = [
  {
    path: 'admin', component: AdminComponent, canActivate: [AdminGuardService], canActivateChild: [AdminGuardService], children: [
      { path: 'validation', component: ValidationComponent, children: [] },
      { path: 'dashboard', component: DashboardComponent, children: [] },
      { path: 'user/:id', component: UserComponent, children: [] },
      { path: 'users', component: UsersComponent, children: [] },
      { path: 'params', component: ParamsComponent, children: [] },
      { path: 'journals', component: JournalsComponent, children: [] },
      { path: 'purge', component: PurgeComponent, children: [] },
      { path: 'groups', component: GroupsComponent, children: [] },
      { path: 'configs', component: ConfigurationComponent, children: [] },
      { path: 'applications', component: ApplicationsComponent, children: [] },
      { path: '', redirectTo: '/logged/admin/dashboard', pathMatch: 'prefix' }
    ]
  },
];

authguard.service (canActivate returns true if the local storage has a token)

@Injectable()
export class AdminGuardService implements CanActivate, CanActivateChild, CanLoad {
  jwtHelper: JwtHelper = new JwtHelper();
  constructor(private router: Router, private alerter: AlertService) { }
  // tslint:disable-next-line:max-line-length
  canActivate(route?: ActivatedRouteSnapshot, state?: RouterStateSnapshot): boolean { return canActivate(route, state, this, [global.roles.admin]); }
  canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { return this.canActivate(route, state); }
  canLoad(route: Route): boolean { console.log('coucou'); return canActivate(null, null, this, [global.roles.admin]); }
}

EDIT The canActivate function, used by the guard (that works well for canActivate and canActivateChild) :

export function canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot, caller: any, userRoles: string[]): boolean {
  try {
    // Get the token
    let token = localStorage.getItem('jwt');

    // If no token found, not connected
    if (!token) {
      caller.alerter.error(`Vous n'êtes pas connecté`);
      caller.router.navigate(['/login']);
      return false;
    }

    // Try to get roles. If it fails, not a valid token.
    let decodedToken = caller.jwtHelper.decodeToken(token);
    let roles: Array<String> = decodedToken.roles.split(',');

    // return true if any role is found
    let found = false;
    for (let role of userRoles) {
      if (roles.indexOf(role) >= 0) { found = true; }
    }
    if (found) { return true; }

    // Role not found => Unauthorized
    caller.alerter.error(`Autorisation manquante`);
    caller.router.navigate(['/logged/main']);
    return false;

  } catch (ex) {
    // Catch the JWT errors
    caller.alerter.error(`La session utilisateur est corrompue`);
    caller.router.navigate(['/login']);
    return false;
  }
}

2 Answers2

2

You don't want to define your lazy loaded modules' sub routes in your main loggedRoutes const. You should only define an entry point to the module, and then let the module handle its interior routing. By defining your modules' sub routes in loggedRoutes you've actually declared that they're part of your root app module.

canLoad is only for use with a lazy loaded module - and those modules must control their own routing. The root module cannot control this because then it would know about the lazy loaded module's components, and it wouldn't be lazy loaded.

Remove this from your loggedRoutes:

...adminRoutes,
...pilotageRoutes,
...suiviProdRoutes,

...mainRoutes is fine as it isn't part of a lazy loaded module.

and ensure your lazy loaded modules have their routes registered:

@NgModule({
  imports: [ RouterModule.forChild(adminRoutes) ]
})
export class AdminModule { }

The lazy loaded modules want to handle their own routing internally, as you're registering all routes at the top level your app does not care about the lazy loaded modules, it will see your admin routes and navigate you there because it's just a normal route. The AOT compiler will see your loadChildrens, however, and it will chunk up your code for you, but your main chunk containing your app will ALSO contain all the extra modules that are supposed to be lazy loaded.

You should also drop the name of the first route from your adminRoutes:

path: 'admin', component: AdminComponent...

Should become

path: '', component: AdminComponent...

The route segment admin is already provided by your module being registered as admin in the main loggedRoutes.

UncleDave
  • 6,872
  • 1
  • 26
  • 44
  • So, deleting the routes seems to work. Removing the `admin` path, not so much ... Now the application has no more routing, it's only going on `admin/dashboard` (default route for admin module). But if I don't remove it, the `dashboard` route isn't considered as default anymore, instead it only shows the adminComponent, which is only a router-outlet ... Any suggestions for that ? (Make another answer if you need to !) –  Nov 09 '17 at 12:49
  • Sorry, `mainRoutes` should have stayed as this is not a lazy loaded module. – UncleDave Nov 09 '17 at 12:51
  • Yeah I fugred this out :P Sorry I forgot to mention it though. But even when I let it in its place, the default route (empty route) that is supposed to redirect to `dashboard`, doesn't work anymore. Do you have an explanation for that ? –  Nov 09 '17 at 12:54
  • Your `redirectTo` in the `AdminModule` may not appreciate being an absolute route, perhaps try changing it to `{ path: '', redirectTo: 'dashboard', pathMatch: 'full' }` Setting `pathMatch` to `'prefix'` when the path is `''` doesn't sound like a good idea either. – UncleDave Nov 09 '17 at 12:55
  • Doesn't seem to work either ... The page is just staying on the admin route, no redirection. But it's not my original issue, I'll try to fix that on my own. You hepled me a lot, thank you, you deserved the bounty ! –  Nov 09 '17 at 13:03
  • ... But you can only have it in 23 hours. Thanks SOF ! –  Nov 09 '17 at 13:04
  • Just so you know, I managed to make it work as you said (deleting the parent paths and using relative routes for the redirect) ! I'll give you the bounty tommorow, thanks a lot for your help on everything :) –  Nov 09 '17 at 13:27
0

There may be a problem with your canActivate. Here is the method body:

return canActivate(route, state, this, [global.roles.admin]);

It is calling another function called canActivate. What's that?

BeetleJuice
  • 39,516
  • 19
  • 105
  • 165
  • As I said, it's just a test with the local storage. But this `canActivate` function works for the `canActivate` and `canActivateChildren`, and since they have the same signature, I guessed that it should work for canLoad. I'm editing my post to put the function in it, see it in 1 minute ! –  Nov 07 '17 at 11:55
  • Done. Moreover, even if canActivate did not work, since the console log is **before** the function call, it should be displayed, right ? –  Nov 07 '17 at 11:57