11

I am confronted with the following issue: the personal section which is protected via an AuthGuard Service is loaded twice when I navigate to it or by browser refresh. Second time it strips away the query parameters of the URL if I provide any. Here is my app router configuration:

const routes: Routes = [
  {
    path: 'search',
    redirectTo: '',
    pathMatch: 'full'
  },
  {
    path: '',
    component: BookmarksComponent
  },
  {
    path: 'tagged/:tag',
    component: TagComponent
  },
  {
    path: 'about',
    component: AboutComponent
  },
  {
    path: 'personal',
    loadChildren: 'app/personal/personal-bookmarks.module#PersonalBookmarksModule'
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {}

and the child router configuration

@NgModule({
  imports: [RouterModule.forChild([
    {
      path: '',
      component: PersonalBookmarksComponent,
      canActivate: [AuthGuard],
      children: [
        {
          path: '',
          component: PersonalBookmarksListComponent
        },
        {
          path: 'new',
          component: NewPersonalBookmarkFormComponent
        },
        {
          path: 'bookmarks/:id',
          component: PersonalBookmarkDetailComponent
        }
      ]
    }

  ])],
  exports: [RouterModule]
})
export class PersonalBookmarksRoutingModule {}

The AuthGuard Service (whereby if it only returns true is the same behaviour):

@Injectable()
export class AuthGuard implements CanActivate {

  constructor(private keycloakService: KeycloakService) {}

  canActivate() {
    console.log('AuthGuard#canActivate called');

    if (this.keycloakService.isLoggedIn()) {
      return true;
    } else {
      this.keycloakService.login();
    }
  }
}

And the Navbar template:

<nav class="navbar navbar-light bg-faded" id="navbar">
  <a class="navbar-brand" [routerLink]="['']" routerLinkActive="active">
    <img src="assets/logo.png" width="35" height="35" class="d-inline-block align-top" alt="">
    Public Bookmarks
  </a>
  <ul class="nav navbar-nav">
    <li class="nav-item">
      <a class="nav-link" [routerLink]="['personal']" routerLinkActive="active">Personal list</a>
    </li>
    <li class="nav-item">
      <a class="nav-link" [routerLink]="['about']" routerLinkActive="active">About</a>
    </li>
    <li class="nav-item">
      <a class="nav-link" href="http://www.codingpedia.org/tags/#codingmarks" target="_blank">Blog</a>
    </li>
    <li *ngIf="isLoggedIn else notLoggedIn" class="nav-item">
      <a class="nav-link" (click)="logout()">Logout <i class="fa fa-lock"></i></a>
    </li>
    <ng-template #notLoggedIn>
      <li *ngIf="!keycloakService.isLoggedIn()" class="nav-item">
        <a class="nav-link" (click)="login()">Login <i class="fa fa-unlock"></i></a>
      </li>
    </ng-template>
  </ul>
</nav>

The project is available on Github and the faulty behaviour can be tested at https://www.codingmarks.org/personal by logging in with username/pwd - test@codingmarks.org/Test_user1$

UPDATE Even if I remove PersonalBookmarksComponent and move AuthGuard to the PersonalBookmarks module the wrong behaviour still persist... The routes for PersonalBookmarksModule look something like the following:

const personalBookmarksRoutes: Routes = [
    {
      path: 'search',
      redirectTo: '',
      pathMatch: 'full'
    },
    {
      path: '',
      component: PersonalBookmarksListComponent,
      canActivate: [AuthGuard],
    },
    {
      path: 'new',
      component: NewPersonalBookmarkFormComponent
    },
    {
      path: 'bookmarks/:id',
      component: PersonalBookmarkDetailComponent
    }

];

UPDATE2: The stripping away of the query parameters was due to the redirection forced when logging in with Keycloak.

False:

public login(): Promise<any> {
  let options: any;
  options = {redirectUri: environment.HOST + 'personal'};
  return new Promise<any>((resolve, reject) => {
    KeycloakService.auth.login(options)
      .success(resolve)
      .error(reject);
  });
}

Correct:

public login(): Promise<any> {
  return new Promise<any>((resolve, reject) => {
    KeycloakService.auth.login()
      .success(resolve)
      .error(reject);
  });
}
ama
  • 1,525
  • 1
  • 16
  • 34
  • Can you try adding a none empty path for `BookmarksComponent` – Robin Dijkhof Sep 05 '17 at 09:02
  • I tried that but got same behaviour, why would it help? – ama Sep 06 '17 at 04:10
  • @ama Did you resolved this issue, If yes, can you post your answer? I'm also facing same issue, The lazy load module, child route calling four times, I'm not using any keycloak or guard either. Simply lazy load module and child components. – Cegone Jul 03 '20 at 06:54
  • and It happened only when I try to navigate to the module by url, like http:/localhost:4200/#/ps/container, It is not happening when I navigate from one route to another route – Cegone Jul 03 '20 at 06:57

2 Answers2

5

The reason the routes are being loaded twice is because you have specified the router path to load two components when you go to navigate to this ' ' route .

change the router configuration to

@NgModule({
  imports: [RouterModule.forChild([
    {
      path: '',
      //component: PersonalBookmarksComponent, //either remove this component line completely [best way is to remove this component deceleration]
      canActivate: [AuthGuard],
      children: [
        {
          path: '', // or specify a different route to this better use a redirect option on path 
          component: PersonalBookmarksListComponent
        },
        {
          path: 'new',
          component: NewPersonalBookmarkFormComponent
        },
        {
          path: 'bookmarks/:id',
          component: PersonalBookmarkDetailComponent
        }
      ]
    }

  ])],
  exports: [RouterModule]
})
Rahul Singh
  • 19,030
  • 11
  • 64
  • 86
  • PersonalBookmarksComponent contains just the router-outlet to render the "personal" child views... It's an approach taken in the official router tutorial too - https://angular.io/generated/live-examples/router/eplnkr.html Still even if I change it to non-empty (leave the child empty), or change the child component to non-empty and leave the parent path empty, the same behaviour still remains... I am starting to think it might have something to do with keycloak js adatper – ama Sep 07 '17 at 04:42
  • @Adrian can you create a small demo replicating this problem in stackblitz, as this should have worked – Rahul Singh Sep 07 '17 at 05:07
3

U can perform the following two steps:

  1. Move the authGuard to the personal module routing, it will also improve your performance as the module will not be loaded in case the user not authenticated.
  2. Remove the "PersonalBookmarksComponent" component containing an empty router outlet, instead provide the router for rest of our components directly.

I believe this approach will solve your problem.

The guard is being called twice once for your component and once for your children component.

Yaroslav Admin
  • 13,880
  • 6
  • 63
  • 83