Lesson 6 — Routing as application state¶
Objective¶
Make feature state addressable through lazy, testable URLs with explicit loading, error, and navigation policy.
Prerequisites¶
Standalone components, DI lifetime, async-state vocabulary, and semantic links.
Mental model¶
Routing is application state encoded in a URL. Matching creates route snapshots, injector boundaries, and component trees. Good URLs survive refresh, deep linking, sharing, and browser history.
Why it exists¶
Routes make workflows addressable and divide loading/lifetime boundaries. Guards manage navigation UX; backend data boundaries still enforce authorization.
Working model: URL matches route and renders component.
Engineering reality: navigation can redirect, cancel, await work, instantiate providers, and load code. Blocking navigation changes latency and failure UX.
Minimal example¶
{ path: 'topics/:id', loadComponent: () => import('./topic-detail.component').then(m => m.TopicDetailComponent) }
With component input binding, route path/query/data values can bind to matching inputs.
Real-world usage¶
Detail pages, editable workflows, permissions UX, query-state search, and feature-scoped services depend on routing semantics.
Common mistakes¶
- treating guards as security enforcement;
- forcing every fetch into a resolver;
- imperative navigation where a link is correct;
- asserting route data exists instead of modeling unknown IDs;
- creating query-parameter synchronization loops.
Mini lab¶
Task: Add lazy dashboard/topics/detail/practice routes, component input binding, derived detail state, titles, wildcard fallback, and a dirty-form CanDeactivateFn.
Constraints: direct refresh and keyboard navigation must work; unknown IDs render intentionally; use a resolver only when navigation must wait.
Expected result: URLs restore the same state, lazy chunks load, and unsaved changes receive navigation protection.
Hint 1
Decide whether data is required before route activation or can be represented as loading after activation.
Hint 2
Use links for destinations and router.navigate for imperative workflow outcomes.
Solution direction — last resort
Lazy-load feature components, enable component input binding, derive the current topic from input plus store state, and model undefined explicitly.
Verification¶
Refresh a detail URL; test valid/unknown IDs and the guard with RouterTestingHarness. Explain why a guard returning true after an authorization call cannot secure backend data.
Mastery evidence: design a deep-linkable route, test navigation outcome, and defend resolver versus in-component loading.
Bloom challenge¶
- Understand: route state and component state relationship.
- Apply: implement and test a lazy detail route.
- Analyze: trace route-level provider lifetime.
- Evaluate: resolver versus explicit loading UI.
- Create: preserve filters in query parameters without synchronization cycles.
Summary¶
Routing combines URL state, code loading, injector lifetime, and navigation UX; it does not replace server authorization.
Spaced review¶
Retrieve provider scope from Lesson 5. Lesson 10 tests route outcomes without coupling to router internals.
What comes next¶
Continue to strictly typed reactive forms.