skip to content

In Angular, what does the Router.currentNavigation() signal expose during a navigation, and how can code abort a pending navigation?

level: middleimportance: nice to knowfreq 26%

answer

  1. a Navigation object or null
  2. initialUrl, finalUrl, trigger, extras
  3. extras.state for passed data
  4. abort() before activation

basics

~20 s

Router.currentNavigation() returns the in-flight Navigation, or null when idle: its id, initialUrl, extractedUrl, finalUrl (set once routes are recognised), trigger, extras such as state, and previousNavigation. Calling its abort() cancels the navigation with the Aborted code, if activation has not started.

solid answer

~30 s

`currentNavigation` is a signal on `Router`, added in v20.2 to replace the deprecated `getCurrentNavigation()`. While a navigation runs it holds a `Navigation` object: `id`, `initialUrl` (what was requested), `extractedUrl`, `finalUrl` (after redirects; guaranteed from `RoutesRecognized` on), `trigger` (`'imperative'`, `'popstate'` or `'hashchange'`), `extras` (the `NavigationExtras`, including `state` passed to `navigate()`), and `previousNavigation`, the last successful one. A routed component often reads `extras.state` in its constructor, while the navigation that created it is still current. Since v20 the object also has `abort()`: calling it before activation begins cancels the navigation with `NavigationCancellationCode.Aborted`; once the router starts activating routes it is a no-op.

code

ts · 27 lines
ts
import { Component, computed, inject } from '@angular/core';
import { Router, RouterOutlet } from '@angular/router';

@Component({
  selector: 'app-root',
  imports: [RouterOutlet],
  template: `
    @if (pending()) {
      <p class="nav-status">Opening {{ pending() }}...
        <button type="button" (click)="cancel()">Cancel</button>
      </p>
    }
    <router-outlet />
  `,
})
export class App {
  private readonly router = inject(Router);
  protected readonly pending = computed(() => {
    const nav = this.router.currentNavigation();
    return nav ? this.router.serializeUrl(nav.finalUrl ?? nav.initialUrl) : null;
  });

  protected cancel(): void {
    // emits NavigationCancel with NavigationCancellationCode.Aborted, unless activation has started
    this.router.currentNavigation()?.abort();
  }
}

go deeper

for a junior

Know that the router exposes the current navigation as a signal that is null when nothing is navigating.

for a middle

Describe the Navigation fields, especially finalUrl timing, trigger and extras.state, and why state must be read during the navigation.

for a senior

Use abort() and trigger deliberately, know when abort is a no-op, and keep extras.state for hints rather than required data.

for a principal

Decide which navigation context is passed through state versus the URL, weighing shareable links and reload behaviour against convenience.

## The signal `Router.currentNavigation` is a **read-only signal** of type `Navigation | null`. The router sets it when a navigation begins, updates it as the navigation progresses (for example once `finalUrl` is known), and resets it to `null` when the navigation finishes in any way. Because it is a signal, `computed()`, `effect()` and templates can react to it. The older `getCurrentNavigation()` method returns the same object without change notification; it has been **deprecated since v20.2**. ## What a `Navigation` contains | Property | Meaning | |---|---| | `id` | the navigation's number, matching the `id` on its router events | | `initialUrl` | the `UrlTree` passed to `navigateByUrl()` or produced by `navigate()`, before redirects | | `extractedUrl` | the part of `initialUrl` the `UrlHandlingStrategy` gives to Angular | | `finalUrl` | the URL after `redirectTo` rules; may be `undefined` early, guaranteed after `RoutesRecognized` | | `trigger` | `'imperative'`, `'popstate'` (back/forward) or `'hashchange'` | | `extras` | the `NavigationExtras` used: `state`, `replaceUrl`, `skipLocationChange`, query-param handling and so on | | `previousNavigation` | the last **successful** navigation, whose own `previousNavigation` is `null` | | `abort()` | cancels this navigation if it has not reached activation | ## Reading navigation state in the destination A common use is reading one-off data passed with `router.navigate(['/reports', id], { state: { from: 'dashboard' } })`. The destination component is created during the navigation's activation phase, while it is still the current navigation, so the constructor can read it: ```ts private readonly from = inject(Router).currentNavigation()?.extras.state?.['from']; ``` By `ngOnInit` time the value may already be gone because the navigation has finished; after that point the same state is available from `history.state` in the browser. The state is **not** part of the URL, so it is lost on a page reload or a shared link, and it should carry hints (where the user came from), not required data. ## Aborting a navigation Since v20, `Navigation.abort()` lets code cancel a navigation in progress: 1. Before activation begins, calling `abort()` stops the navigation; the router emits `NavigationCancel` with **`NavigationCancellationCode.Aborted`** and the `navigate()` promise resolves `false`. 2. Once the router has started activating the new routes, `abort()` is a **no-op**, because a half-activated route tree cannot be rolled back. 3. After the navigation completes, the router replaces `abort` with a no-op function. Use cases include a "Cancel" button next to a slow resolver's loading bar, or dropping a navigation when a modal dialog opens. For ordinary "the user clicked somewhere else" cases there is no need to abort: the router cancels the older navigation itself with `SupersededByNewNavigation`. ## The companion signal: `lastSuccessfulNavigation` `Router.lastSuccessfulNavigation` holds the `Navigation` object of the most recent navigation that **succeeded**, or `null` before the first one. It became a signal in v21 (before that it was a plain property), so code written for v20 that reads it as a value needs to call it. The two signals answer different questions: | Signal | Value while navigating | Value when idle | |---|---|---| | `currentNavigation()` | the in-flight navigation | `null` | | `lastSuccessfulNavigation()` | the previous successful navigation | the navigation that produced the current page | So a page that wants to know how the user arrived (for example whether it was a back-button navigation) after the navigation has finished can read `lastSuccessfulNavigation()?.trigger`, and state passed with the navigation can still be found on its `extras`. ## Other uses of the signal - **Loading indicators**: `computed(() => router.currentNavigation() !== null)`. - **Back/forward detection**: `trigger === 'popstate'` lets a page skip an entry animation or restore a list position. - **Where the user is heading**: `finalUrl` during a guard or a pending state lets a banner say where the user is going. Keep in mind that the signal describes **one** navigation at a time: when a newer navigation supersedes an older one, it holds the newer navigation.

  • Why can the destination component read extras.state in its constructor but not reliably in a later click handler?
    Routed components are created during the activation phase, while their navigation is still the router's current navigation, so `currentNavigation()` returns it in the constructor. Once the navigation finishes, the signal is reset to `null`, so later code gets nothing and should read `history.state` instead, or better, receive the data another way.
  • What happens if abort() is called after activation has started?
    Nothing. The router marks the navigation as no longer abortable just before it activates routes, because partially activated components cannot be rolled back cleanly. The navigation continues and ends with `NavigationEnd`; once it completes, `abort` is replaced with a no-op function.

saying these in an interview costs you the question

  • currentNavigation() still returns the navigation after NavigationEnd
  • finalUrl is always set as soon as NavigationStart fires
  • Navigation state passed via extras.state survives a page reload
  • abort() can undo a navigation after its components were activated
  • getCurrentNavigation() is the recommended API in current Angular