Search code examples
angularlazy-loadingangular-routingangular-lazyloading

Angular - Lazy load conditional route


I am building an application, where I authenticate the user at the start of the application. Based on the whether the user has been authenticated or not, I want to lazy load a different angular module.

In context:

app.component.ts

@Component({
  template: `
    <app-splash-screen *ngIf="isAuthenticating$ | async; else main"></app-splash-screen>

    <ng-template #main>
      <router-outlet></router-outlet>
    </ng-template>
  `
})
export class AppComponent implements OnInit {
  readonly isAuthenticating$ = this.authFacade.isAuthenticating$;

  constructor(private readonly authFacade: AuthFacade) {}

  ngOnInit(): void {
    this.authFacade.authenticate();
  }
}

This is where I initiate my authentication logic that completes at an uncertain point in time. I can opt-in after completion with an Observable, if I need to.

When authentication has completed I load the router-outlet to the DOM.


app-routing.module.ts

const routes: Routes = [
  // Lazy-load, when authentication succeeds
  {
    path: '',
    pathMatch: 'full',
    loadChildren: () => import('@my-application/secret').then(m => m.SecretModule)
  },

  // Lazy-load, when authentication fails
  {
    path: 'home',
    loadChildren: () => import('@my-application/home').then(m => m.HomeModule)
  },
];

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

These are my top-level routes. Now, what I want to achieve is when the user has been authenticated, to lazy-load the SecretModule and display its contents.

On the other hand, when authentication fails, I want to navigate the user to the home page and lazy-load the HomeModule.

Now I see a problem regarding the blank route for the SecretModule. One work-around that I could think of is to implement this conditional routing logic elsewhere manually after authentication has completed, instead of doing it the 'angular way'.

What do you think might be the best approach here to implement said logic?

(I am using Angular 11 by the way)


Solution

  • First you can add two guard so unauthorized users can't load and access your secret module:

    auth.guard.ts

    @Injectable({
      providedIn: 'root'
    })
    export class AuthGuard implements CanActivate, CanLoad {
      constructor(
        private router: Router,
        private authService: AuthService, // I asume u got some kind of auth service written
      ) {}
    
      canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean | UrlTree> | boolean | UrlTree {
        console.log('Trying to activate admin route');
        return this.checkAccess();
      }
    
      canLoad(route: Route, segments: UrlSegment[]): Promise<boolean | UrlTree> | boolean | UrlTree {
        console.log('Trying to load admin  route');
        return this.checkAccess();
      }
    
      private checkAccess(): Promise<boolean | UrlTree> {
        return new Promise<boolean | UrlTree>(async (resolve) => {
          // Do your check here if the user is authenticated
          const isLoggedIn = await this.authService.isLoggedIn();
    
          // If user is not authenticated he gets redirected to home page
          resolve(isLoggedIn ? true : this.router.createUrlTree(['home']));
        });
      }
    }
    

    Then simply use your guards on your secret route:

    const routes: Routes = [
      // Lazy-load, when authentication succeeds
      {
        path: '',
        pathMatch: 'full',
        loadChildren: () => import('@my-application/secret').then(m => m.SecretModule),
        canLoad: [AuthGuard],
        canActivate: [AuthGuard],
      },
    
      // Lazy-load, when authentication fails
      {
        path: 'home',
        loadChildren: () => import('@my-application/home').then(m => m.HomeModule)
      },
    ];
    

    You can read more on guard in the docs