Thomas Perrin
Thomas Perrin

Reputation: 784

Angular 12 router.navigate in guards throws "Cannot activate an already activated outlet" error when hitting url directly instead of normal navigation

I have an Angular 12 app with a lazy loaded module. The different children routes are protected by some different guards. I can navigate as expected through the app without any problem, but if I directly hit the url of any of the children of the lazy loaded module, then I got in the console the error "Cannot activate an already activated outlet" and the app craches.

I targeted the problem in my guard, if I comment the this.router.navigate(["/shop"]); line of my guard, then I don't get the error (but then I don't have the expected behavior if the guard: it blocks well the page but don't navigate to another url...)

I tried the solution below but it doesn't solve the error (even if it concerns also a lazy loading module, the problem was about named outlets which I don't have) Angular 7 - Multiple outlets : Error: Cannot activate an already activated outlet

Here is my root routing file app.routes.ts with the 2 lazy modules for the whole app, which is imported in the app.module.ts imports by the classic RouterModule.forRoot(APP_ROUTING)`

// file: app.routes.ts
// native modules
import { Routes } from "@angular/router";

export const APP_ROUTING: Routes = [
  // empty url redirects top the shop
  { path: "", redirectTo: "shop", pathMatch: "full" },

  // shop as lazy loaded module
  {
    path: "shop",
    loadChildren: () => import("./shop/shop.module").then(m => m.ShopModule)
  },

  // backoffice as lazy lloaded module
  {
    path: "back",
    loadChildren: () =>
      import("./backoffice/backoffice.module").then(m => m.BackofficeModule)
  },

  // Any non existing url redirects to the shop
  { path: "**", redirectTo: "shop" }
];

Here are the routes of the lazy module:

// shop.routes.ts file
export const SHOP_ROUTES: Routes = [
  {
    path: "",
    component: ShopWorkspaceComponent,

    // prettier-ignore
    children: [
      { path: '', component: HomepageComponent },
      { path: 'connexion', canActivate:[AnonymousGuard], component: ShopLoginComponent },
      { path: 'inscription', canActivate:[AnonymousGuard], component: ShopRegisterComponent },
      { path: 'profil', canActivate:[ClientGuard], component: ShopProfileComponent }
    ]
  }
];

Here is the AnonymousGuard canActivate:

  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean> {
    return this.authStore.pipe(
      withLatestFrom(this.authStore.pipe(select(isAnonymousSelector))),
      map(([, isAnonymous]) => {
        if (isAnonymous) return true;
        else {
          // if I comment the router.navigate below I don't have the error
          this.router.navigate(["/shop"]);
          return false;
        }
      })
    );
  }
}

and one of the lazy module with the Router.forChild (minus imports):

// shop.module.ts file, lazy loaded    
const COMPONENTS = [
  ShopWorkspaceComponent,
  HomepageComponent,
  ShopMenuComponent,
  ShopRegisterComponent
]

const MODULES = [
  // native modules
  RouterModule.forChild(SHOP_ROUTES),
  BasicModule,

  // feature modules
  ShopLoginModule,
  ShopProfileModule,

];

// Shop module
@NgModule({
  declarations: COMPONENTS,
  imports: MODULES
})
export class ShopModule {}

Upvotes: 0

Views: 968

Answers (1)

YogendraR
YogendraR

Reputation: 2396

Try updating your canActivate like this:

canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
return this.authStore.pipe(
  withLatestFrom(this.authStore.pipe(select(isAnonymousSelector))),
  map(([, isAnonymous]) => {
    if (isAnonymous) return true;
    else {
      return this.router.navigate(["../shop"]);
     }
   })
  );
 }
}

Upvotes: 0

Related Questions