lazy-loaded child route loads twice in Angular
Asked Answered
M

2

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 - [email protected]/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);
  });
}
Mcnew answered 28/8, 2017 at 7:35 Comment(4)
Can you try adding a none empty path for BookmarksComponentStrigose
I tried that but got same behaviour, why would it help?Mcnew
@Mcnew 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.Electrosurgery
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 routeElectrosurgery
I
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]
})
Inhumanity answered 6/9, 2017 at 5:27 Comment(2)
PersonalBookmarksComponent contains just the router-outlet to render the "personal" child views... It's an approach taken in the official router tutorial too - 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 adatperMcnew
@Adrian can you create a small demo replicating this problem in stackblitz, as this should have workedInhumanity
W
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.

Watercool answered 9/9, 2017 at 13:31 Comment(1)
Hey Chirag, that did not do it either - see the update in my question...Mcnew

© 2022 - 2024 — McMap. All rights reserved.