Angular Routing - Quick Reference
Deep Knowledge: Use mcp__documentation__fetch_docs with technology: angular, topic: routing for comprehensive documentation.
Route Configuration (Standalone)
// app.routes.ts
import { Routes } from '@angular/router';
export const routes: Routes = [
{ path: '', redirectTo: 'dashboard', pathMatch: 'full' },
{
path: 'dashboard',
loadComponent: () => import('./features/dashboard/dashboard.component')
.then(m => m.DashboardComponent),
},
{
path: 'users',
loadChildren: () => import('./features/users/users.routes')
.then(m => m.USERS_ROUTES),
canActivate: [authGuard],
},
{ path: '**', loadComponent: () => import('./not-found.component').then(m => m.NotFoundComponent) },
];
Lazy Loading Feature Routes
// features/users/users.routes.ts
import { Routes } from '@angular/router';
export const USERS_ROUTES: Routes = [
{
path: '',
loadComponent: () => import('./user-list.component').then(m => m.UserListComponent),
},
{
path: ':id',
loadComponent: () => import('./user-detail.component').then(m => m.UserDetailComponent),
resolve: { user: userResolver },
},
];
Functional Guards (Angular 15+)
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { AuthService } from '../services/auth.service';
export const authGuard: CanActivateFn = () => {
const authService = inject(AuthService);
const router = inject(Router);
if (authService.isAuthenticated()) {
return true;
}
return router.createUrlTree(['/login']);
};
export const roleGuard: CanActivateFn = (route) => {
const authService = inject(AuthService);
const requiredRole = route.data['role'] as string;
return authService.hasRole(requiredRole);
};
Functional Resolvers
import { inject } from '@angular/core';
import { ResolveFn } from '@angular/router';
import { UserService } from './user.service';
import { User } from './user.model';
export const userResolver: ResolveFn<User> = (route) => {
const userService = inject(UserService);
const id = Number(route.paramMap.get('id'));
return userService.getById(id);
};
Route Parameters with Input Binding
// Enable in app.config.ts: provideRouter(routes, withComponentInputBinding())
@Component({ standalone: true, template: `<p>User {{ id }}</p>` })
export class UserDetailComponent {
@Input() id!: string; // Automatically bound from :id route param
}
Navigation
import { Router, RouterLink } from '@angular/router';
// Template
// <a [routerLink]="['/users', user.id]">View User</a>
// <a routerLink="/dashboard" routerLinkActive="active">Dashboard</a>
// Programmatic
@Component({ ... })
export class SomeComponent {
private router = inject(Router);
navigate() {
this.router.navigate(['/users', 42], {
queryParams: { tab: 'profile' },
});
}
}
Anti-Patterns
| Anti-Pattern | Why It's Bad | Correct Approach |
|---|
| Eager loading all routes | Large bundle size | Use loadComponent / loadChildren |
| Class-based guards | Verbose, deprecated pattern | Use functional guards with inject() |
| Subscribing to params manually | Boilerplate, leak risk | Use withComponentInputBinding() |
| Nested route configs in app.routes | Hard to maintain | Split into feature route files |
Quick Troubleshooting
| Issue | Likely Cause | Solution |
|---|
| Route not matching | Order matters | Put specific routes before wildcards |
| Guard not triggering | Not in route config | Add to canActivate array |
| Lazy load fails | Wrong export | Ensure component is exported |
| Query params lost | Navigate without preserve | Use queryParamsHandling: 'merge' |