mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(router): add type properties to all router events (#44189)
All router events now have a `type` property with a string name identifying the specific type of the event. The `type` property can be used to perform TypeScript type narrowing using an `if` statement, `switch` statement, or similar. This removes the need to perform `instanceof` checks but still allows them if preferred. An `instanceof` check requires the use of the event class value which may not be available or preferred in certain situations. The Router's `enableTracing` option is also now guarded with the `ngDevMode` flag which combined with the use of a new `stringifyEvent` function allows the tree-shaking of the string generation code for each router event class. However, the original `toString` class methods are still present to prevent a potential breaking change. As a result, full benefits of the tree-shaking potential will not be realized until they are removed. PR Close #44189
This commit is contained in:
committed by
Dylan Hunn
parent
ea2b3a9dfa
commit
41e2a68e30
@@ -20,6 +20,30 @@ import {ActivatedRouteSnapshot, RouterStateSnapshot} from './router_state';
|
||||
*/
|
||||
export type NavigationTrigger = 'imperative'|'popstate'|'hashchange';
|
||||
|
||||
/**
|
||||
* Identifies the type of a router event.
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export const enum EventType {
|
||||
NavigationStart,
|
||||
NavigationEnd,
|
||||
NavigationCancel,
|
||||
NavigationError,
|
||||
RoutesRecognized,
|
||||
ResolveStart,
|
||||
ResolveEnd,
|
||||
GuardsCheckStart,
|
||||
GuardsCheckEnd,
|
||||
RouteConfigLoadStart,
|
||||
RouteConfigLoadEnd,
|
||||
ChildActivationStart,
|
||||
ChildActivationEnd,
|
||||
ActivationStart,
|
||||
ActivationEnd,
|
||||
Scroll,
|
||||
}
|
||||
|
||||
/**
|
||||
* Base for events the router goes through, as opposed to events tied to a specific
|
||||
* route. Fired one time for any given navigation.
|
||||
@@ -58,6 +82,8 @@ export class RouterEvent {
|
||||
* @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()`.
|
||||
@@ -66,7 +92,7 @@ export class NavigationStart extends RouterEvent {
|
||||
* @see `NavigationCancel`
|
||||
* @see `NavigationError`
|
||||
*/
|
||||
navigationTrigger?: 'imperative'|'popstate'|'hashchange';
|
||||
navigationTrigger?: NavigationTrigger;
|
||||
|
||||
/**
|
||||
* The navigation state that was previously supplied to the `pushState` call,
|
||||
@@ -93,7 +119,7 @@ export class NavigationStart extends RouterEvent {
|
||||
/** @docsNotRequired */
|
||||
url: string,
|
||||
/** @docsNotRequired */
|
||||
navigationTrigger: 'imperative'|'popstate'|'hashchange' = 'imperative',
|
||||
navigationTrigger: NavigationTrigger = 'imperative',
|
||||
/** @docsNotRequired */
|
||||
restoredState: {[k: string]: any, navigationId: number}|null = null) {
|
||||
super(id, url);
|
||||
@@ -117,6 +143,8 @@ export class NavigationStart extends RouterEvent {
|
||||
* @publicApi
|
||||
*/
|
||||
export class NavigationEnd extends RouterEvent {
|
||||
readonly type = EventType.NavigationEnd;
|
||||
|
||||
constructor(
|
||||
/** @docsNotRequired */
|
||||
id: number,
|
||||
@@ -146,6 +174,8 @@ export class NavigationEnd extends RouterEvent {
|
||||
* @publicApi
|
||||
*/
|
||||
export class NavigationCancel extends RouterEvent {
|
||||
readonly type = EventType.NavigationCancel;
|
||||
|
||||
constructor(
|
||||
/** @docsNotRequired */
|
||||
id: number,
|
||||
@@ -172,6 +202,8 @@ export class NavigationCancel extends RouterEvent {
|
||||
* @publicApi
|
||||
*/
|
||||
export class NavigationError extends RouterEvent {
|
||||
readonly type = EventType.NavigationError;
|
||||
|
||||
constructor(
|
||||
/** @docsNotRequired */
|
||||
id: number,
|
||||
@@ -194,6 +226,8 @@ export class NavigationError extends RouterEvent {
|
||||
* @publicApi
|
||||
*/
|
||||
export class RoutesRecognized extends RouterEvent {
|
||||
readonly type = EventType.RoutesRecognized;
|
||||
|
||||
constructor(
|
||||
/** @docsNotRequired */
|
||||
id: number,
|
||||
@@ -221,6 +255,8 @@ export class RoutesRecognized extends RouterEvent {
|
||||
* @publicApi
|
||||
*/
|
||||
export class GuardsCheckStart extends RouterEvent {
|
||||
readonly type = EventType.GuardsCheckStart;
|
||||
|
||||
constructor(
|
||||
/** @docsNotRequired */
|
||||
id: number,
|
||||
@@ -247,6 +283,8 @@ export class GuardsCheckStart extends RouterEvent {
|
||||
* @publicApi
|
||||
*/
|
||||
export class GuardsCheckEnd extends RouterEvent {
|
||||
readonly type = EventType.GuardsCheckEnd;
|
||||
|
||||
constructor(
|
||||
/** @docsNotRequired */
|
||||
id: number,
|
||||
@@ -278,6 +316,8 @@ export class GuardsCheckEnd extends RouterEvent {
|
||||
* @publicApi
|
||||
*/
|
||||
export class ResolveStart extends RouterEvent {
|
||||
readonly type = EventType.ResolveStart;
|
||||
|
||||
constructor(
|
||||
/** @docsNotRequired */
|
||||
id: number,
|
||||
@@ -303,6 +343,8 @@ export class ResolveStart extends RouterEvent {
|
||||
* @publicApi
|
||||
*/
|
||||
export class ResolveEnd extends RouterEvent {
|
||||
readonly type = EventType.ResolveEnd;
|
||||
|
||||
constructor(
|
||||
/** @docsNotRequired */
|
||||
id: number,
|
||||
@@ -329,6 +371,8 @@ export class ResolveEnd extends RouterEvent {
|
||||
* @publicApi
|
||||
*/
|
||||
export class RouteConfigLoadStart {
|
||||
readonly type = EventType.RouteConfigLoadStart;
|
||||
|
||||
constructor(
|
||||
/** @docsNotRequired */
|
||||
public route: Route) {}
|
||||
@@ -345,6 +389,8 @@ export class RouteConfigLoadStart {
|
||||
* @publicApi
|
||||
*/
|
||||
export class RouteConfigLoadEnd {
|
||||
readonly type = EventType.RouteConfigLoadEnd;
|
||||
|
||||
constructor(
|
||||
/** @docsNotRequired */
|
||||
public route: Route) {}
|
||||
@@ -362,6 +408,8 @@ export class RouteConfigLoadEnd {
|
||||
* @publicApi
|
||||
*/
|
||||
export class ChildActivationStart {
|
||||
readonly type = EventType.ChildActivationStart;
|
||||
|
||||
constructor(
|
||||
/** @docsNotRequired */
|
||||
public snapshot: ActivatedRouteSnapshot) {}
|
||||
@@ -379,6 +427,8 @@ export class ChildActivationStart {
|
||||
* @publicApi
|
||||
*/
|
||||
export class ChildActivationEnd {
|
||||
readonly type = EventType.ChildActivationEnd;
|
||||
|
||||
constructor(
|
||||
/** @docsNotRequired */
|
||||
public snapshot: ActivatedRouteSnapshot) {}
|
||||
@@ -397,6 +447,8 @@ export class ChildActivationEnd {
|
||||
* @publicApi
|
||||
*/
|
||||
export class ActivationStart {
|
||||
readonly type = EventType.ActivationStart;
|
||||
|
||||
constructor(
|
||||
/** @docsNotRequired */
|
||||
public snapshot: ActivatedRouteSnapshot) {}
|
||||
@@ -415,6 +467,8 @@ export class ActivationStart {
|
||||
* @publicApi
|
||||
*/
|
||||
export class ActivationEnd {
|
||||
readonly type = EventType.ActivationEnd;
|
||||
|
||||
constructor(
|
||||
/** @docsNotRequired */
|
||||
public snapshot: ActivatedRouteSnapshot) {}
|
||||
@@ -430,6 +484,8 @@ export class ActivationEnd {
|
||||
* @publicApi
|
||||
*/
|
||||
export class Scroll {
|
||||
readonly type = EventType.Scroll;
|
||||
|
||||
constructor(
|
||||
/** @docsNotRequired */
|
||||
readonly routerEvent: NavigationEnd,
|
||||
@@ -479,5 +535,60 @@ export class Scroll {
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export type Event = RouterEvent|RouteConfigLoadStart|RouteConfigLoadEnd|ChildActivationStart|
|
||||
ChildActivationEnd|ActivationStart|ActivationEnd|Scroll;
|
||||
export type Event =
|
||||
RouterEvent|NavigationStart|NavigationEnd|NavigationCancel|NavigationError|RoutesRecognized|
|
||||
GuardsCheckStart|GuardsCheckEnd|RouteConfigLoadStart|RouteConfigLoadEnd|ChildActivationStart|
|
||||
ChildActivationEnd|ActivationStart|ActivationEnd|Scroll|ResolveStart|ResolveEnd;
|
||||
|
||||
|
||||
export function stringifyEvent(routerEvent: Event): string {
|
||||
if (!('type' in routerEvent)) {
|
||||
return `Unknown Router Event: ${routerEvent.constructor.name}`;
|
||||
}
|
||||
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.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}')`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
export {RouterLink, RouterLinkWithHref} from './directives/router_link';
|
||||
export {RouterLinkActive} from './directives/router_link_active';
|
||||
export {RouterOutlet, RouterOutletContract} from './directives/router_outlet';
|
||||
export {ActivationEnd, ActivationStart, ChildActivationEnd, ChildActivationStart, Event, GuardsCheckEnd, GuardsCheckStart, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, ResolveEnd, ResolveStart, RouteConfigLoadEnd, RouteConfigLoadStart, RouterEvent, RoutesRecognized, Scroll} from './events';
|
||||
export {ActivationEnd, ActivationStart, ChildActivationEnd, ChildActivationStart, Event, EventType, GuardsCheckEnd, GuardsCheckStart, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, ResolveEnd, ResolveStart, RouteConfigLoadEnd, RouteConfigLoadStart, RouterEvent, RoutesRecognized, Scroll} from './events';
|
||||
export {CanActivate, CanActivateChild, CanDeactivate, CanLoad, Data, LoadChildren, LoadChildrenCallback, QueryParamsHandling, Resolve, ResolveData, Route, Routes, RunGuardsAndResolvers, UrlMatcher, UrlMatchResult} from './models';
|
||||
export {DefaultTitleStrategy, TitleStrategy} from './page_title_strategy';
|
||||
export {BaseRouteReuseStrategy, DetachedRouteHandle, RouteReuseStrategy} from './route_reuse_strategy';
|
||||
|
||||
@@ -15,7 +15,7 @@ import {EmptyOutletComponent} from './components/empty_outlet';
|
||||
import {RouterLink, RouterLinkWithHref} from './directives/router_link';
|
||||
import {RouterLinkActive} from './directives/router_link_active';
|
||||
import {RouterOutlet} from './directives/router_outlet';
|
||||
import {Event} from './events';
|
||||
import {Event, stringifyEvent} from './events';
|
||||
import {Route, Routes} from './models';
|
||||
import {DefaultTitleStrategy, TitleStrategy} from './page_title_strategy';
|
||||
import {RouteReuseStrategy} from './route_reuse_strategy';
|
||||
@@ -473,11 +473,11 @@ export function setupRouter(
|
||||
|
||||
assignExtraOptionsToRouter(opts, router);
|
||||
|
||||
if (opts.enableTracing) {
|
||||
if ((typeof ngDevMode === 'undefined' || ngDevMode) && opts.enableTracing) {
|
||||
router.events.subscribe((e: Event) => {
|
||||
// tslint:disable:no-console
|
||||
console.group?.(`Router Event: ${(<any>e.constructor).name}`);
|
||||
console.log(e.toString());
|
||||
console.log(stringifyEvent(e));
|
||||
console.log(e);
|
||||
console.groupEnd?.();
|
||||
// tslint:enable:no-console
|
||||
|
||||
Reference in New Issue
Block a user