Course outline

Angular / Intermediate / Lesson 8

Router navigation and route data

Treat the URL as application state, configure ordered routes and outlets, and validate route data without treating a client guard as server authorization.

About 5 min readContent reviewed
On this page

Lesson goals

  • Explain route matching, outlets, links, and parameters.
  • Validate route identifiers before domain use.
  • Handle navigation changes and denied access explicitly.

Navigation should have a reproducible address

The router maps URL state to views displayed in outlets. Route configuration, a provided router, a RouterOutlet, and navigation links participate in that contract. A page that works only after clicking through another page is incomplete if its URL is intended to be directly loadable. Path parameters, query parameters, and fragments have different roles; choose which state belongs in each instead of hiding all navigation in service fields. Browser back and forward should restore a meaningful view. Server hosting must also serve the correct application or route response for a direct request, which later deployment lessons distinguish from client matching.

A route parameter is text from a boundary

The complete model accepts a small canonical positive identifier string and rejects malformed input before converting it. A route parameter is not automatically numeric merely because the path uses :id. A numeric conversion alone can accept whitespace or other spellings the domain may not permit. The model makes its accepted syntax and safe integer range explicit. It tests local parsing only, not Angular matching or navigation. The server still needs to validate and authorize the actual requested record; a valid number is not evidence that the actor may view it.

typescript Complete local route-identifier parser
export {};
function courseId(text: string | null): number | undefined {
  if (text === null || !/^[1-9][0-9]*$/.test(text)) { return undefined; }
  const id = Number(text);
  return Number.isSafeInteger(id) && id <= 100000 ? id : undefined;
}
console.log(courseId("7"), courseId("7x"), courseId(null));
Expected output
7 undefined undefined

The line is 7 undefined undefined. No router, navigation event, or server record is accessed.

Order route declarations and provide the outlet

The contextual configuration assumes standalone CoursePage and NotFoundPage components exist. The empty path redirects only on a full match, the parameterized route selects the course page, and the wildcard appears last. The application must provide these routes with provideRouter, and the shell must import RouterOutlet before rendering its outlet. RouterLink similarly needs its directive import in the owning standalone template. This fragment does not create the referenced pages or a browser host. Its expected matching behavior can be checked in an isolated router fixture, including direct links and the fallback route.

typescript Contextual ordered route configuration
import { Routes } from "@angular/router";
import { CoursePage } from "./course-page";
import { NotFoundPage } from "./not-found-page";
export const routes: Routes = [
  { path: "", redirectTo: "courses/1", pathMatch: "full" },
  { path: "courses/:id", component: CoursePage },
  { path: "**", component: NotFoundPage }
];

With the stated pages and router bootstrap, /courses/7 would match CoursePage and an unknown path would reach the fallback. Navigation is not executed by this fragment.

Do not assume the component is recreated for every parameter

A router can reuse a component while its parameter values change. Reading a snapshot once is appropriate only when the feature contract truly needs that one moment; otherwise observe the parameter stream or a supported input-binding mechanism and derive current state. Keep subscriptions tied to the owner lifecycle. Loading a new record needs loading, error, and empty handling, and stale responses should not overwrite a newer navigation result. Guards and resolvers have documented return and cancellation behavior that should be tested. A guard can improve navigation experience, but it cannot secure an API against direct calls. Authorization belongs on the server.

Test the address, view, and permission contract

Test initial navigation, parameter changes, query updates, unknown paths, redirect loops, and browser-history behavior. A route unit test does not prove that the deployed server supports refresh on that deep URL. Use an isolated router host to verify the outlet and accessible navigation text. Do not construct URLs by concatenating unvalidated fragments when router commands can represent segments clearly. When navigation is denied, explain the resulting view and focus behavior instead of leaving the user at a silent blank screen. Keep local parsing results separate from actual router and server behavior in validation reports.

Common errors and fixes

An empty-path redirect catches every navigation.

Why it happens
The redirect lacks the intended full-match policy.
How to fix it
Set pathMatch: "full" for the declared empty redirect.
Prevent it next time
Test both root and nonempty paths.

The page still shows the previous route identifier.

Why it happens
A snapshot was treated as a continuously updated source.
How to fix it
Observe current route state with lifecycle-managed updates.
Prevent it next time
Test parameter changes without forcing component recreation.

A guard is used as the only permission check.

Why it happens
Client navigation control was confused with server authorization.
How to fix it
Authorize the protected operation on the server.
Prevent it next time
Test direct denied API access separately.

Check your understanding

1. Classify "0", "007", "12", and "12x" with the local parser. Explain why this is a domain policy rather than an Angular requirement.

Show worked solution

Only "12" is accepted. The parser deliberately requires a canonical positive decimal without leading zeroes and a bounded safe integer. Angular itself can route text containing other spellings; this validator chooses what the application accepts. If the product permits leading zeroes, change and test that normalization policy explicitly rather than weakening it through a cast.

2. A user moves from /courses/7 to /courses/8 while the page component is reused. Name the state and test concerns beyond updating the heading.

Show worked solution

Update the validated identifier, start the new loading state, prevent a late result for seven from replacing eight, and show a deliberate error or missing-record outcome. Test route-stream cleanup and the visible record identity after both responses. Server permission must be checked for eight even if seven was authorized. A changed heading alone does not prove the loaded data matches the current address.

Key takeaways

  • The URL is state with a validation boundary.
  • Match routes deliberately and observe changing parameters.
  • Guards guide navigation; servers enforce permissions.

Official references