/
githubmirror
/
angular
Обзор
Документация
Войти
/
githubmirror
/
angular
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
packages/router/src/events.ts
758 строк
22 KB
SkyZeroZx
docs(docs-infra): Add build-time validation for API and guide links using route manifest
08 июл 2026, 20:24
08 июл 2026, 20:24
c1829f6
Код
Авторство
О чём код?
/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.dev/license */ import {NavigationBehaviorOptions, Route} from './models'; import type {Navigation} from './navigation_transition'; import {ActivatedRouteSnapshot, RouterStateSnapshot} from './router_state'; import {UrlTree} from './url_tree'; /** * Identifies the call or event that triggered a navigation. * * * 'imperative': Triggered by `router.navigateByUrl()` or `router.navigate()`. * * 'popstate' : Triggered by a `popstate` event. * * 'hashchange'-: Triggered by a `hashchange` event. * * @publicApi */ export type NavigationTrigger = 'imperative' | 'popstate' | 'hashchange'; export const IMPERATIVE_NAVIGATION = 'imperative'; /** * Identifies the type of a router event. * * @see [Router Lifecycle and Events](guide/routing/lifecycle-and-events) * * @publicApi */ export enum EventType { NavigationStart, NavigationEnd, NavigationCancel, NavigationError, RoutesRecognized, ResolveStart, ResolveEnd, GuardsCheckStart, GuardsCheckEnd, RouteConfigLoadStart, RouteConfigLoadEnd, ChildActivationStart, ChildActivationEnd, ActivationStart, ActivationEnd, Scroll, NavigationSkipped, } /** * Base for events the router goes through, as opposed to events tied to a specific * route. Fired one time for any given navigation. * * The following code shows how a class subscribes to router events. * * ```ts * import {Event, RouterEvent, Router} from '@angular/router'; * * class MyService { * constructor(public router: Router) { * router.events.pipe( * filter((e: Event | RouterEvent): e is RouterEvent => e instanceof RouterEvent) * ).subscribe((e: RouterEvent) => { * // Do something * }); * } * } * ``` * * @see {@link Event} * @see [Router events summary](guide/routing/router-reference#router-events) * @publicApi */ export class RouterEvent { constructor( /** A unique ID that the router assigns to every router navigation. */ public id: number, /** The URL that is the destination for this navigation. */ public url: string, ) {} } /** * An event triggered when a navigation starts. * * @publicApi */ export class NavigationStart extends RouterEvent { readonly type = EventType.NavigationStart; /** * Identifies the call or event that triggered the navigation. * An `imperative` trigger is a call to `router.navigateByUrl()` or `router.navigate()`. * * @see {@link NavigationEnd} * @see {@link NavigationCancel} * @see {@link NavigationError} */ navigationTrigger?: NavigationTrigger; /** * The navigation state that was previously supplied to the `pushState` call, * when the navigation is triggered by a `popstate` event. Otherwise null. * * The state object is defined by `NavigationExtras`, and contains any * developer-defined state value, as well as a unique ID that * the router assigns to every router transition/navigation. * * From the perspective of the router, the router never "goes back". * When the user clicks on the back button in the browser, * a new navigation ID is created. * * Use the ID in this previous-state object to differentiate between a newly created * state and one returned to by a `popstate` event, so that you can restore some * remembered state, such as scroll position. * */ restoredState?: {[k: string]: any; navigationId: number} | null; constructor( /** @docsNotRequired */ id: number, /** @docsNotRequired */ url: string, /** @docsNotRequired */ navigationTrigger: NavigationTrigger = 'imperative', /** @docsNotRequired */ restoredState: {[k: string]: any; navigationId: number} | null = null, ) { super(id, url); this.navigationTrigger = navigationTrigger; this.restoredState = restoredState; } /** @docs-private */ override toString(): string { return `NavigationStart(id: ${this.id}, url: '${this.url}')`; } } /** * An event triggered when a navigation ends successfully. * * @see {@link NavigationStart} * @see {@link NavigationCancel} * @see {@link NavigationError} * * @publicApi */ export class NavigationEnd extends RouterEvent { readonly type = EventType.NavigationEnd; constructor( /** @docsNotRequired */ id: number, /** @docsNotRequired */ url: string, /** @docsNotRequired */ public urlAfterRedirects: string, ) { super(id, url); } /** @docs-private */ override toString(): string { return `NavigationEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}')`; } } /** * A code for the `NavigationCancel` event of the `Router` to indicate the * reason a navigation failed. * * @publicApi */ export enum NavigationCancellationCode { /** * A navigation failed because a guard returned a `UrlTree` to redirect. */ Redirect, /** * A navigation failed because a more recent navigation started. */ SupersededByNewNavigation, /** * A navigation failed because one of the resolvers completed without emitting a value. */ NoDataFromResolver, /** * A navigation failed because a guard returned `false`. */ GuardRejected, /** * A navigation was aborted by the `Navigation.abort` function. * * @see {@link Navigation} */ Aborted, } /** * A code for the `NavigationSkipped` event of the `Router` to indicate the * reason a navigation was skipped. * * @publicApi */ export enum NavigationSkippedCode { /** * A navigation was skipped because the navigation URL was the same as the current Router URL. */ IgnoredSameUrlNavigation, /** * A navigation was skipped because the configured `UrlHandlingStrategy` return `false` for both * the current Router URL and the target of the navigation. * * @see {@link UrlHandlingStrategy} */ IgnoredByUrlHandlingStrategy, } /** * An event triggered when a navigation is canceled, directly or indirectly. * This can happen for several reasons including when a route guard * returns `false` or initiates a redirect by returning a `UrlTree`. * * @see {@link NavigationStart} * @see {@link NavigationEnd} * @see {@link NavigationError} * * @publicApi */ export class NavigationCancel extends RouterEvent { readonly type = EventType.NavigationCancel; constructor( /** @docsNotRequired */ id: number, /** @docsNotRequired */ url: string, /** * A description of why the navigation was cancelled. For debug purposes only. Use `code` * instead for a stable cancellation reason that can be used in production. */ public reason: string, /** * A code to indicate why the navigation was canceled. This cancellation code is stable for * the reason and can be relied on whereas the `reason` string could change and should not be * used in production. */ readonly code?: NavigationCancellationCode, ) { super(id, url); } /** @docs-private */ override toString(): string { return `NavigationCancel(id: ${this.id}, url: '${this.url}')`; } } export function isRedirectingEvent(event: Event): boolean { return ( event instanceof NavigationCancel && (event.code === NavigationCancellationCode.Redirect || event.code === NavigationCancellationCode.SupersededByNewNavigation) ); } /** * An event triggered when a navigation is skipped. * This can happen for a couple reasons including onSameUrlHandling * is set to `ignore` and the navigation URL is not different than the * current state. * * @publicApi */ export class NavigationSkipped extends RouterEvent { readonly type = EventType.NavigationSkipped; constructor( /** @docsNotRequired */ id: number, /** @docsNotRequired */ url: string, /** * A description of why the navigation was skipped. For debug purposes only. Use `code` * instead for a stable skipped reason that can be used in production. */ public reason: string, /** * A code to indicate why the navigation was skipped. This code is stable for * the reason and can be relied on whereas the `reason` string could change and should not be * used in production. */ readonly code?: NavigationSkippedCode, ) { super(id, url); } } /** * An event triggered when a navigation fails due to an unexpected error. * * @see {@link NavigationStart} * @see {@link NavigationEnd} * @see {@link NavigationCancel} * * @publicApi */ export class NavigationError extends RouterEvent { readonly type = EventType.NavigationError; constructor( /** @docsNotRequired */ id: number, /** @docsNotRequired */ url: string, /** @docsNotRequired */ public error: any, /** * The target of the navigation when the error occurred. * * Note that this can be `undefined` because an error could have occurred before the * `RouterStateSnapshot` was created for the navigation. */ readonly target?: RouterStateSnapshot, ) { super(id, url); } /** @docs-private */ override toString(): string { return `NavigationError(id: ${this.id}, url: '${this.url}', error: ${this.error})`; } } /** * An event triggered when routes are recognized. * * @publicApi */ export class RoutesRecognized extends RouterEvent { readonly type = EventType.RoutesRecognized; constructor( /** @docsNotRequired */ id: number, /** @docsNotRequired */ url: string, /** @docsNotRequired */ public urlAfterRedirects: string, /** @docsNotRequired */ public state: RouterStateSnapshot, ) { super(id, url); } /** @docs-private */ override toString(): string { return `RoutesRecognized(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`; } } /** * An event triggered at the start of the Guard phase of routing. * * @see {@link GuardsCheckEnd} * * @publicApi */ export class GuardsCheckStart extends RouterEvent { readonly type = EventType.GuardsCheckStart; constructor( /** @docsNotRequired */ id: number, /** @docsNotRequired */ url: string, /** @docsNotRequired */ public urlAfterRedirects: string, /** @docsNotRequired */ public state: RouterStateSnapshot, ) { super(id, url); } /** @docs-private */ override toString(): string { return `GuardsCheckStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`; } } /** * An event triggered at the end of the Guard phase of routing. * * @see {@link GuardsCheckStart} * * @publicApi */ export class GuardsCheckEnd extends RouterEvent { readonly type = EventType.GuardsCheckEnd; constructor( /** @docsNotRequired */ id: number, /** @docsNotRequired */ url: string, /** @docsNotRequired */ public urlAfterRedirects: string, /** @docsNotRequired */ public state: RouterStateSnapshot, /** @docsNotRequired */ public shouldActivate: boolean, ) { super(id, url); } /** @docs-private */ override toString(): string { return `GuardsCheckEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state}, shouldActivate: ${this.shouldActivate})`; } } /** * An event triggered at the start of the Resolve phase of routing. * * Runs in the "resolve" phase whether or not there is anything to resolve. * In future, may change to only run when there are things to be resolved. * * @see {@link ResolveEnd} * * @publicApi */ export class ResolveStart extends RouterEvent { readonly type = EventType.ResolveStart; constructor( /** @docsNotRequired */ id: number, /** @docsNotRequired */ url: string, /** @docsNotRequired */ public urlAfterRedirects: string, /** @docsNotRequired */ public state: RouterStateSnapshot, ) { super(id, url); } /** @docs-private */ override toString(): string { return `ResolveStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`; } } /** * An event triggered at the end of the Resolve phase of routing. * @see {@link ResolveStart} * * @publicApi */ export class ResolveEnd extends RouterEvent { readonly type = EventType.ResolveEnd; constructor( /** @docsNotRequired */ id: number, /** @docsNotRequired */ url: string, /** @docsNotRequired */ public urlAfterRedirects: string, /** @docsNotRequired */ public state: RouterStateSnapshot, ) { super(id, url); } /** @docs-private */ override toString(): string { return `ResolveEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`; } } /** * An event triggered before lazy loading a route configuration. * * @see {@link RouteConfigLoadEnd} * * @publicApi */ export class RouteConfigLoadStart { readonly type = EventType.RouteConfigLoadStart; constructor( /** @docsNotRequired */ public route: Route, ) {} /** @docs-private */ toString(): string { return `RouteConfigLoadStart(path: ${this.route.path})`; } } /** * An event triggered when a route has been lazy loaded. * * @see {@link RouteConfigLoadStart} * * @publicApi */ export class RouteConfigLoadEnd { readonly type = EventType.RouteConfigLoadEnd; constructor( /** @docsNotRequired */ public route: Route, ) {} /** @docs-private */ toString(): string { return `RouteConfigLoadEnd(path: ${this.route.path})`; } } /** * An event triggered at the start of the child-activation * part of the Resolve phase of routing. * @see {@link ChildActivationEnd} * @see {@link ResolveStart} * * @publicApi */ export class ChildActivationStart { readonly type = EventType.ChildActivationStart; constructor( /** @docsNotRequired */ public snapshot: ActivatedRouteSnapshot, ) {} /** @docs-private */ toString(): string { const path = (this.snapshot.routeConfig && this.snapshot.routeConfig.path) || ''; return `ChildActivationStart(path: '${path}')`; } } /** * An event triggered at the end of the child-activation part * of the Resolve phase of routing. * @see {@link ChildActivationStart} * @see {@link ResolveStart} * @publicApi */ export class ChildActivationEnd { readonly type = EventType.ChildActivationEnd; constructor( /** @docsNotRequired */ public snapshot: ActivatedRouteSnapshot, ) {} /** @docs-private */ toString(): string { const path = (this.snapshot.routeConfig && this.snapshot.routeConfig.path) || ''; return `ChildActivationEnd(path: '${path}')`; } } /** * An event triggered at the start of the activation part * of the Resolve phase of routing. * @see {@link ActivationEnd} * @see {@link ResolveStart} * * @publicApi */ export class ActivationStart { readonly type = EventType.ActivationStart; constructor( /** @docsNotRequired */ public snapshot: ActivatedRouteSnapshot, ) {} /** @docs-private */ toString(): string { const path = (this.snapshot.routeConfig && this.snapshot.routeConfig.path) || ''; return `ActivationStart(path: '${path}')`; } } /** * An event triggered at the end of the activation part * of the Resolve phase of routing. * @see {@link ActivationStart} * @see {@link ResolveStart} * * @publicApi */ export class ActivationEnd { readonly type = EventType.ActivationEnd; constructor( /** @docsNotRequired */ public snapshot: ActivatedRouteSnapshot, ) {} /** @docs-private */ toString(): string { const path = (this.snapshot.routeConfig && this.snapshot.routeConfig.path) || ''; return `ActivationEnd(path: '${path}')`; } } /** * An event triggered by scrolling. * * @publicApi */ export class Scroll { readonly type = EventType.Scroll; constructor( /** @docsNotRequired */ readonly routerEvent: NavigationEnd | NavigationSkipped, /** @docsNotRequired */ readonly position: [number, number] | null, /** @docsNotRequired */ readonly anchor: string | null, /** @docsNotRequired */ readonly scrollBehavior?: 'manual' | 'after-transition', ) {} /** @docs-private */ toString(): string { const pos = this.position ? `${this.position[0]}, ${this.position[1]}` : null; return `Scroll(anchor: '${this.anchor}', position: '${pos}')`; } } export class BeforeActivateRoutes {} export class BeforeRoutesRecognized {} export class RedirectRequest { constructor( readonly url: UrlTree, readonly navigationBehaviorOptions: NavigationBehaviorOptions | undefined, ) {} } export type PrivateRouterEvents = BeforeActivateRoutes | RedirectRequest | BeforeRoutesRecognized; export function isPublicRouterEvent(e: Event | PrivateRouterEvents): e is Event { return ( !(e instanceof BeforeActivateRoutes) && !(e instanceof RedirectRequest) && !(e instanceof BeforeRoutesRecognized) ); } /** * Router events that allow you to track the lifecycle of the router. * * The events occur in the following sequence: * * * [NavigationStart](api/router/NavigationStart): Navigation starts. * * [RouteConfigLoadStart](api/router/RouteConfigLoadStart): Before * the router [lazy loads](guide/routing/loading-strategies) a route configuration. * * [RouteConfigLoadEnd](api/router/RouteConfigLoadEnd): After a route has been lazy loaded. * * [RoutesRecognized](api/router/RoutesRecognized): When the router parses the URL * and the routes are recognized. * * [GuardsCheckStart](api/router/GuardsCheckStart): When the router begins the *guards* * phase of routing. * * [ChildActivationStart](api/router/ChildActivationStart): When the router * begins activating a route's children. * * [ActivationStart](api/router/ActivationStart): When the router begins activating a route. * * [GuardsCheckEnd](api/router/GuardsCheckEnd): When the router finishes the *guards* * phase of routing successfully. * * [ResolveStart](api/router/ResolveStart): When the router begins the *resolve* * phase of routing. * * [ResolveEnd](api/router/ResolveEnd): When the router finishes the *resolve* * phase of routing successfully. * * [ChildActivationEnd](api/router/ChildActivationEnd): When the router finishes * activating a route's children. * * [ActivationEnd](api/router/ActivationEnd): When the router finishes activating a route. * * [NavigationEnd](api/router/NavigationEnd): When navigation ends successfully. * * [NavigationCancel](api/router/NavigationCancel): When navigation is canceled. * * [NavigationError](api/router/NavigationError): When navigation fails * due to an unexpected error. * * [Scroll](api/router/Scroll): When the user scrolls. * * @publicApi */ export type Event = | NavigationStart | NavigationEnd | NavigationCancel | NavigationError | RoutesRecognized | GuardsCheckStart | GuardsCheckEnd | RouteConfigLoadStart | RouteConfigLoadEnd | ChildActivationStart | ChildActivationEnd | ActivationStart | ActivationEnd | Scroll | ResolveStart | ResolveEnd | NavigationSkipped; export function stringifyEvent(routerEvent: Event): string { switch (routerEvent.type) { case EventType.ActivationEnd: return `ActivationEnd(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`; case EventType.ActivationStart: return `ActivationStart(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`; case EventType.ChildActivationEnd: return `ChildActivationEnd(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`; case EventType.ChildActivationStart: return `ChildActivationStart(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`; case EventType.GuardsCheckEnd: return `GuardsCheckEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state}, shouldActivate: ${routerEvent.shouldActivate})`; case EventType.GuardsCheckStart: return `GuardsCheckStart(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`; case EventType.NavigationCancel: return `NavigationCancel(id: ${routerEvent.id}, url: '${routerEvent.url}')`; case EventType.NavigationSkipped: return `NavigationSkipped(id: ${routerEvent.id}, url: '${routerEvent.url}')`; case EventType.NavigationEnd: return `NavigationEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}')`; case EventType.NavigationError: return `NavigationError(id: ${routerEvent.id}, url: '${routerEvent.url}', error: ${routerEvent.error})`; case EventType.NavigationStart: return `NavigationStart(id: ${routerEvent.id}, url: '${routerEvent.url}')`; case EventType.ResolveEnd: return `ResolveEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`; case EventType.ResolveStart: return `ResolveStart(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`; case EventType.RouteConfigLoadEnd: return `RouteConfigLoadEnd(path: ${routerEvent.route.path})`; case EventType.RouteConfigLoadStart: return `RouteConfigLoadStart(path: ${routerEvent.route.path})`; case EventType.RoutesRecognized: return `RoutesRecognized(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`; case EventType.Scroll: const pos = routerEvent.position ? `${routerEvent.position[0]}, ${routerEvent.position[1]}` : null; return `Scroll(anchor: '${routerEvent.anchor}', position: '${pos}')`; } }