Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Routes Guards causing wrong Route Navigation behavior

I'm having an issue trying to navigate through different routes.

I have two different route modules.

app.routes.ts:

Containing only LoginPage:

export const routes: Routes = [
  {
    path: 'login',
    component: LoginPageComponent,
    canActivate: [PreventLoggedInAccess]
  },
  {
    path: '',
    redirectTo: 'login',
    pathMatch: 'full'
  },
  {
    path: '**',
    redirectTo: 'login'
  }
];

export const Routing: ModuleWithProviders = 
    RouterModule.forRoot(routes, { useHash : true });

With PreventLoggedInAccess.canActivate, that, if the user is already logged in redirects him to the logged in section with /app prefix and child route home. It's defined as:

canActivate(): boolean {
  if (!this._authService.isAuthenticated()) {
      return true;
  }
  this._router.navigate(['/app/home']);
  return false;
}

pages.routes.ts:

Containing all /app subroutes that are accessible only if the user is logged in. This is achieved using AuthGuardService.canActivateChild:

export const pageRoutes: Routes = [
  {
    path: 'app',
    component: PagesComponent,
    canActivateChild: [AuthGuardService],
    children: [
      { path: '', redirectTo: 'home', pathMatch: 'full' },
      { path: 'home', component: HomePageComponent },
      { path: 'contents', component: ContentsComponent },
    ]
  }
];

export const Routing: ModuleWithProviders = RouterModule.forChild(pageRoutes);

With the latter that redirects to /login if the user isn't logged in. It's defined as:

canActivateChild(): boolean {
  if (this._authService.isAuthenticated()) {
      return true;
  }
  this._router.navigate(['login']);
  return false;
}

When I navigate from app/home to app/contents it only goes to ContentsComponent after navigating two times. So, if I do two times this._router.navigate(['app/components']); it works, if I do it only one time, the route changes from app/home to app/route for 1ms and it returns to app/home, while if I do it a second time it changes route. While, if I'm in app/contents and try to navigate to app/home it changes route just fine.

isAuthenticated works fine. Both authguards work just fine, so, if I try to access any app child route when I'm not logged In I get redirected to login and If I try to access login when I'm logged In I get redirected to app/home.

I managed to debug a bit and I noticed the following flow:

  • First attempt - app/home -> app/contents:
    • navigate(['app/contents']) is called
    • PreventLoggedInAccess.canActivate is called
    • AuthGuardService.canActivateChild is called
  • Second attempt - app/home -> app/contents:
    • navigate(['app/contents']) is called
    • AuthGuardService.canActivateChild is called

Of course the expected behavior is the second one.

EDIT

Removing this._router.navigate([/app/home]); from PreventLoggedInAccess.canActivate solves the issue

canActivate(): boolean {
  if (!this._authService.isAuthenticated()) {
      return true;
  }
  return false;
}

But still, I do not understand why PreventLoggedInAccess.canActivate is called when navigating to an app child even though AuthGuardService.canActivateChild is attached to it? Why is it called only on the first attempt?

like image 393
AndreaM16 Avatar asked Sep 13 '17 10:09

AndreaM16


People also ask

What is the purpose of a route Guard?

This route guard is used to keep the user from navigating away from a specific route. This guard can be useful when you want to prevent a user from accidentally navigating away without saving or some other undone tasks.

What property of a route do we use to implement a guard on a route?

CanActivatelinkInterface that a class can implement to be a guard deciding if a route can be activated. If all guards return true , navigation continues. If any guard returns false , navigation is cancelled.

What are the angular 2 route guards?

Angular route guards are interfaces provided by Angular which, when implemented, allow us to control the accessibility of a route based on conditions provided in class implementation of that interface. Here are some types of Angular guards: CanActivate, CanActivateChild, CanLoad, CanDeactivate and Resolve.


1 Answers

You have mentioned pageRoute can have all subchild of app so its routing file should contain that module.

So that concept of lazy loading and guards can be properly used. I have answered this by assuming that your pageRoute module is child of App.

I suggest to use the AuthGuard only one time. AuthGaurd should be used on module containing other modules or components and not be on login component itself.

Here page module is lazy loaded and can be activated only if authguard returns true In case of false user will navigated to login.

app.route.ts

const routes: Routes = [
  {
    path: '',
    loadChildren: './pages/pages.module#PagesModule',
    canActivate: [AuthGuardService]
  },
  { path: 'login', loadChildren: './login/login.module#LoginModule' },

];

AuthGuard

canActivateChild(): boolean {
  if (this._authService.isAuthenticated()) {
      return true;
  }
  this._router.navigate(['/login']);
  return false;
}

Page.route.ts

const routes: Routes = [
  {
    path: '', component: PageComponent,
     children: [
  { path: '', redirectTo: 'home', pathMatch: 'full' },
  { path: 'home', component: HomePageComponent },
  { path: 'contents', component: ContentsComponent },


     ]
  }
];

In case you want to debug in current routing condition you can use Augury from the Chrome web store.

like image 167
Rajat Gupta Avatar answered Sep 17 '22 12:09

Rajat Gupta