Routing
Routing, guards & resolvers
Lazy loading, functional guards, resolvers and route-level data.
The router maps URL paths to components rendered in <router-outlet>. Lazy loading with loadComponent/loadChildren splits the bundle so a route’s code downloads only when visited.
The router is airport operations: gates (routes) open only when a flight is due (lazy load), and security (guards) checks your boarding pass before you reach the gate.
Key concepts
1
Modern functional guards (CanActivateFn, CanMatchFn) are plain functions that use inject() — lighter than the old class guards. CanMatch can even hide a route entirely (useful for feature flags and role-based routing).
functional guardsCanActivateFnCanMatchFninject()CanMatch
2
Resolvers pre-fetch data before activation so the component renders with data in hand; combine with route data for static metadata and titles.
Resolversdata
3
Pitfall: reading paramMap from a snapshot means it will not update when navigating between sibling params — subscribe to the observable instead. Interview follow-up: "how do you protect a route and still keep the bundle small?" — CanMatch + loadComponent.
Pitfall:Interview follow-up:paramMapCanMatchloadComponent
typescript
export const authGuard: CanActivateFn = () => {
const auth = inject(AuthService);
return auth.isLoggedIn() || inject(Router).createUrlTree(['/login']);
};
export const routes: Routes = [
{ path: 'users/:id',
loadComponent: () => import('./user').then(m => m.UserComponent),
canActivate: [authGuard],
resolve: { user: (r: ActivatedRouteSnapshot) => inject(UserService).byId(r.params['id']) } },
];