Routing

Chapter 10
Routing — Routes, Params, and Guards
Multiple pages in a single-page app — built in, where React needed React Router as a separate library

Routing was a separate library install in React (React Router, Intermediate Chapter 5). In Angular it's part of the framework — the same single-page navigation concepts apply directly, just with Angular's own API. This chapter covers defining routes, linking and navigating, reading URL parameters, and protecting routes with guards.

Defining Routes

// app.routes.ts import { Routes } from '@angular/router'; import { HomeComponent } from './home.component'; import { AboutComponent } from './about.component'; export const routes: Routes = [ { path: '', component: HomeComponent }, { path: 'about', component: AboutComponent }, { path: 'product/:id', component: ProductDetailComponent }, { path: '**', component: NotFoundComponent }, ];

Routes are an array of objects mapping a path to a component — the direct equivalent of React Router's <Route> elements. :id marks a dynamic segment (matching /product/anything), and '**' is the catch-all for any unmatched URL — both familiar from React Router, just expressed as data rather than JSX. This array is wired into the app's configuration once, in main.ts, via provideRouter(routes).

RouterOutlet and RouterLink

// app.component.ts import { RouterOutlet, RouterLink } from '@angular/router'; @Component({ selector: 'app-root', standalone: true, imports: [RouterOutlet, RouterLink], template: ` <nav> <a routerLink="/">Home</a> <a routerLink="/about">About</a> </nav> <router-outlet></router-outlet> `, }) export class AppComponent {}

<router-outlet> is the placeholder where the matched route's component renders — the equivalent of React Router's <Outlet /> (or the <Routes> block itself). routerLink="/about" navigates without a full page reload, exactly like React's <Link to> — and the same warning applies: a plain <a href> would trigger a real reload and defeat the point.

Reading Route Parameters

// product-detail.component.ts import { Component, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; export class ProductDetailComponent { private route = inject(ActivatedRoute); id = this.route.snapshot.paramMap.get('id'); }

ActivatedRoute — injected like any service (Chapter 6) — exposes the current route's parameters. snapshot.paramMap.get('id') reads the :id segment from the URL, the equivalent of React Router's useParams(). The snapshot form is the simplest; for a route that the same component stays on while only the parameter changes, subscribing to route.paramMap (an Observable, Chapter 11) reacts to those changes — but snapshot suffices for most cases.

Programmatic Navigation

import { Router } from '@angular/router'; export class SomeComponent { private router = inject(Router); goToProduct(id: number) { this.router.navigate(['/product', id]); } }

Injecting the Router service gives navigate([...]) for navigating from code — after a form submits, a login succeeds, an action completes — the equivalent of React Router's useNavigate(). The path is passed as an array of segments, which Angular joins into the URL.

Route Guards — Protecting a Route

// auth.guard.ts import { CanActivateFn, Router } from '@angular/router'; import { inject } from '@angular/core'; export const authGuard: CanActivateFn = () => { const auth = inject(AuthService); const router = inject(Router); if (auth.isLoggedIn()) return true; return router.createUrlTree(['/login']); // redirect instead of allowing access };
// in the routes array { path: 'dashboard', component: DashboardComponent, canActivate: [authGuard] }

A guard is a function that runs before a route activates, returning true to allow it or redirecting otherwise — there's no built-in equivalent in React Router itself (it's typically hand-rolled with a wrapper component). Added to a route via canActivate: [authGuard], this is the standard way to keep an unauthenticated user out of a protected page, redirecting them to login instead. Guards inject services freely, since they run inside Angular's DI context.

Lazy loading is built into the route config
Angular pairs code splitting (the React equivalent was Advanced Chapter 3's lazy/Suspense) directly with routing: { path: 'admin', loadComponent: () => import('./admin.component').then(m => m.AdminComponent) } loads that component's code only when its route is first visited. No separate Suspense boundary is needed — the router handles the loading transition itself.
Route order matters — put the wildcard last
The router matches routes top to bottom and stops at the first match, so the catch-all '**' route must be the last entry in the array — placed earlier, it would match everything and prevent any route below it from ever being reached. The same ordering discipline applied to React Router's path="*".
AngularReact Router equivalent
routes array + provideRouter<Routes> / <Route>
<router-outlet><Outlet />
routerLink="/x"<Link to="/x">
ActivatedRoute paramMapuseParams()
Router.navigate([...])useNavigate()
canActivate guardHand-rolled protected-route wrapper

Coding Challenges

Challenge 1

Set up a 3-page app (Home, About, Contact) with a routes array, a nav using routerLink, a router-outlet, and a wildcard route showing a NotFound component for unmatched URLs.

📄 View solution
Challenge 2

Add a product/:id route. From a product list, use routerLink (or Router.navigate) to go to a detail page that reads the id via ActivatedRoute and displays the matching product.

📄 View solution
Challenge 3

Build an authGuard (CanActivateFn) backed by a simple AuthService with an isLoggedIn flag, protecting a /dashboard route — redirecting to /login when not logged in — and a button toggling the logged-in state to test both outcomes.

📄 View solution

Chapter 10 Quick Reference

  • routes array (path → component), wired via provideRouter(routes) — built into Angular
  • <router-outlet> renders the matched route; routerLink navigates without reload
  • :id dynamic segments read via ActivatedRoute.snapshot.paramMap.get('id')
  • Router.navigate([...]) — programmatic navigation from code
  • canActivate: [guard] — a function gating a route, allowing or redirecting
  • loadComponent in a route — lazy-loads that component's code on first visit
  • The '**' wildcard route must be last; matching is top-to-bottom
  • Next chapter: HTTP client and RxJS — talking to a backend with Observables