← Angular: components, reactivity and application decisions
07 / 10 · 65 MIN

Routing, state and navigation decisions

Interpret matching, parameters and navigation decisions when state can change within one instance. Define loading, leaving and failure without confusing local route control with remote authorization.

Matching and explicit order

Angular uses the first applicable match in the route array. A specific route such as orders/new should precede orders/:id so new is not captured as id. The ** wildcard follows routes that should have a chance to match.

Titles and class names do not repair incorrect ordering. In the example, each route declares title to update the page title when activated. The unknown-route view is part of the application; defining an appropriate HTTP status for a server response is an additional delivery responsibility.

Path, query and selection identity

The path orders/:id declares an identity segment. In /orders/R7?tab=history, id belongs to route parameters and tab to the query. Queries suit state such as filters, ordering and pagination, but should be validated before use as business rules.

To change sort while retaining page, queryParamsHandling: merge applies the update to existing parameters. Preserving parameters has a specific intent; it does not mean indiscriminately carrying sensitive state between all pages. Decide which values belong in the resource’s shareable address.

Snapshot or reactive observation

A snapshot represents state at one moment. Storing id in a variable during construction does not make that variable follow later navigation. An instance reused between R7 and R8 must observe updated parameters when its contract requires following selection.

The example uses paramMap and AsyncPipe, producing text for each observed value. If remote loading is involved, compose that source with an appropriate acceptance policy such as replacing obsolete reads. Changing readonly or the variable name creates no reactive connection.

Relative navigation and nested views

RouterLink supplies declarative links and Router.navigate supplies code-driven navigation commands. With relativeTo pointing to /orders/R7, edit without a leading slash can resolve to /orders/R7/edit. A leading slash makes the command absolute and ignores that relative context.

For children routes, the parent component needs RouterOutlet to render the child view at the intended location. Configuration and presentation trees must agree. Do not interpret a missing outlet as proof that the URL or server rejected access.

Guards return decisions

CanActivate decides client-side route access; CanDeactivate can consult the current instance before leaving, for example when a draft is pending. CanMatch false has a specific consequence: it allows other matching routes to be tried. To redirect, return UrlTree or RedirectCommand instead of false followed by another imperative navigation.

An Observable result uses its first emission and then unsubscribes. None of these contracts guarantees authorization of requests sent directly to an API. The application must define server checks for each protected operation and resource.

Guard concurrency and dependencies

A guard array establishes start order and result priority but does not guarantee an asynchronous guard finishes before the next begins. If checkRole depends on data loaded by loadUser, placing loadUser first does not prove that dependency. Combine the sequence in a guard that controls it or use a service providing coordinated shared loading.

This differs from merely adding a delay hoping data arrives. Review should identify when required information becomes available and which result takes priority when decisions differ.

Resolvers, waiting and observable failure

A resolver obtains data before activation and exposes it through route state. This can avoid rendering a component without required data, but moves waiting into navigation. Consider feedback during that wait and bound mandatory work.

An unhandled failure can produce NavigationError; define centralized handling, event handling or an explicit resolver decision. Returning an empty object to make an error disappear is correct only if that object actually meets the contract.

It does not communicate loading success when required data remains absent.

// Reading example: local routes and displayed identity; no application bootstrap.
import {AsyncPipe} from '@angular/common';
import {ApplicationConfig, Component, inject} from '@angular/core';
import {ActivatedRoute, RouterLink, RouterOutlet, Routes, provideRouter} from '@angular/router';
import {map} from 'rxjs';
@Component({selector: 'new-order', standalone: true, template: '<h1>New order</h1>'})
export class NewOrder {}
@Component({
  selector: 'order-detail', standalone: true, imports: [AsyncPipe],
  template: `<h1>Order {{ id$ | async }}</h1>`,
})
export class OrderDetail {
  private readonly route = inject(ActivatedRoute);
  readonly id$ = this.route.paramMap.pipe(map(params => params.get('id') ?? 'missing'));
}
@Component({selector: 'unknown-route', standalone: true, template: '<h1>Unknown route</h1>'})
export class UnknownRoute {}
export const routes: Routes = [
  {path: 'orders/new', component: NewOrder, title: 'Create an order'},
  {path: 'orders/:id', component: OrderDetail, title: 'Order details'},
  {path: '**', component: UnknownRoute, title: 'Unknown route'},
];
export const appConfig: ApplicationConfig = {providers: [provideRouter(routes)]};
@Component({
  selector: 'order-app', standalone: true, imports: [RouterLink, RouterOutlet],
  template: `<nav>
    <a routerLink="/orders/new">New</a>
    <a [routerLink]="['/orders', 'R7']">R7</a>
    <a [routerLink]="['/orders', 'R8']">R8</a>
  </nav><router-outlet />`,
})
export class OrderApp {}
IN PRACTICE

orders/new precedes orders/:id, so new is not interpreted as an identifier along that path. OrderDetail follows paramMap through AsyncPipe instead of retaining only the initial read. The page displays received identity only; it neither fetches an order nor confirms authorization.

The wildcard renders a local view and does not by itself define a server HTTP status.

Common pitfalls

Placing wildcards before specific routes; confusing query with path; expecting updates to a captured snapshot variable; combining false with imperative navigation for redirects; depending on sequential async guard completion; converting resolver failure into empty success.

Related topics: Components, templates and view identity · HTTP, streams and response acceptance · Security boundaries and trusted content

Take this idea with you

Configuration defines how a route is found, but observed state and navigation policy evolve over time. Match data to current identity, make data waiting explicit and treat failures as distinct outcomes. Guards remain client-side decisions.

Create account

Reference: Route matching order · Angular 22; standalone components