Date: May 4, 2023
Introduction
Building a scalable Angular application often means splitting your app into multiple pages or feature modules, protecting certain routes based on user authentication, and optimizing performance with lazy loading.
In this tutorial, we’ll create a multi-page Angular app featuring:
- Router configuration
- Lazy loading of feature modules
- Route guards to protect authenticated and unauthenticated routes
By the end, you’ll understand how to structure your app for maintainability and security.
Step 1: Setup Angular Project and Routing
Create a new Angular project with routing enabled:
ng new multi-page-app --routing
cd multi-page-app
The CLI generates an app-routing.module.ts for central routing setup.
Step 2: Create Feature Modules for Pages
Let’s create two feature modules — Home and Dashboard.
ng generate module features/home --route home --module app.module
ng generate module features/dashboard --route dashboard --module app.module
This command sets up lazy-loaded routes for /home and /dashboard.
Step 3: Understanding Lazy Loading
Lazy loading means Angular will only load the code for a module when the user navigates to that route, improving initial load time.
Generated app-routing.module.ts looks like:
const routes: Routes = [
{
path: 'home',
loadChildren: () => import('./features/home/home.module').then(m => m.HomeModule)
},
{
path: 'dashboard',
loadChildren: () => import('./features/dashboard/dashboard.module').then(m => m.DashboardModule)
},
{ path: '', redirectTo: 'home', pathMatch: 'full' },
];
Step 4: Create Route Guards
Route guards help control access to routes.
Generate an AuthGuard to protect routes that require authentication:
ng generate guard guards/auth
Implement the guard in auth.guard.ts:
import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(private router: Router) {}
isAuthenticated(): boolean {
// Replace with real auth check logic
return !!localStorage.getItem('userToken');
}
canActivate(): boolean {
if (this.isAuthenticated()) {
return true;
}
this.router.navigate(['/home']);
return false;
}
}
Step 5: Protect Routes Using the Guard
Update dashboard route to use the guard:
{
path: 'dashboard',
loadChildren: () => import('./features/dashboard/dashboard.module').then(m => m.DashboardModule),
canActivate: [AuthGuard]
}
Now, users can access /dashboard only if authenticated.
Step 6: Create an Unauthenticated Guard (Optional)
Similarly, create a guard to prevent logged-in users from visiting public pages like login:
ng generate guard guards/no-auth
canActivate(): boolean {
if (!this.isAuthenticated()) {
return true;
}
this.router.navigate(['/dashboard']);
return false;
}
Use on /home or login route as needed.
Step 7: Navigating Between Pages
Use Angular’s routerLink for navigation:
<nav>
<a routerLink="/home">Home</a>
<a routerLink="/dashboard">Dashboard</a>
</nav>
<router-outlet></router-outlet>
Bonus: Route Guard Testing
You can write simple unit tests for guards using Jasmine:
describe('AuthGuard', () => {
let guard: AuthGuard;
let routerSpy = jasmine.createSpyObj('Router', ['navigate']);
beforeEach(() => {
guard = new AuthGuard(routerSpy);
});
it('should allow activation when authenticated', () => {
localStorage.setItem('userToken', 'token');
expect(guard.canActivate()).toBeTrue();
});
it('should redirect to home when not authenticated', () => {
localStorage.removeItem('userToken');
expect(guard.canActivate()).toBeFalse();
expect(routerSpy.navigate).toHaveBeenCalledWith(['/home']);
});
});
Summary
In this tutorial, we covered:
- Setting up lazy-loaded routes with Angular Router
- Creating and applying route guards for authentication
- Basic navigation and guard testing
This setup lays the foundation for scalable and secure Angular apps.