@angular/router
Version:
Angular - the routing library
1,597 lines (1,588 loc) • 51.5 kB
JavaScript
/**
* @license Angular v21.2.0
* (c) 2010-2026 Google LLC. https://angular.dev/
* License: MIT
*/
import * as i3 from '@angular/common';
import { ViewportScroller, PlatformNavigation, PlatformLocation, ɵPRECOMMIT_HANDLER_SUPPORTED as _PRECOMMIT_HANDLER_SUPPORTED, Location, ɵNavigationAdapterForLocation as _NavigationAdapterForLocation, LOCATION_INITIALIZED, LocationStrategy, HashLocationStrategy, PathLocationStrategy } from '@angular/common';
import * as i0 from '@angular/core';
import { inject, signal, Injectable, HostAttributeToken, linkedSignal, untracked, ɵINTERNAL_APPLICATION_ERROR_HANDLER as _INTERNAL_APPLICATION_ERROR_HANDLER, effect, ɵRuntimeError as _RuntimeError, computed, booleanAttribute, Directive, Attribute, Input, HostListener, EventEmitter, ContentChildren, Output, createEnvironmentInjector, InjectionToken, NgZone, EnvironmentInjector, DestroyRef, afterNextRender, ɵpromiseWithResolvers as _promiseWithResolvers, ɵpublishExternalGlobalUtil as _publishExternalGlobalUtil, makeEnvironmentProviders, APP_BOOTSTRAP_LISTENER, provideEnvironmentInitializer, Injector, ApplicationRef, ɵIS_ENABLED_BLOCKING_INITIAL_NAVIGATION as _IS_ENABLED_BLOCKING_INITIAL_NAVIGATION, provideAppInitializer, ɵperformanceMarkFeature as _performanceMarkFeature, ENVIRONMENT_INITIALIZER, NgModule } from '@angular/core';
import { Router, StateManager, UrlSerializer, NavigationEnd, UrlTree, ROUTER_CONFIGURATION, isUrlTree, ActivatedRoute, isActive, exactMatchOptions, subsetMatchOptions, RouterConfigLoader, IMPERATIVE_NAVIGATION, NavigationTransitions, NavigationStart, NavigationSkipped, NavigationSkippedCode, Scroll, BeforeRoutesRecognized, BeforeActivateRoutes, NavigationCancel, NavigationError, isRedirectingEvent, NavigationCancellationCode, ROUTES, afterNextNavigation, ROUTE_INJECTOR_CLEANUP, routeInjectorCleanup, stringifyEvent, NAVIGATION_ERROR_HANDLER, RoutedComponentInputBinder, INPUT_BINDER, CREATE_VIEW_TRANSITION, createViewTransition, VIEW_TRANSITION_OPTIONS, DefaultUrlSerializer, ChildrenOutletContexts, RouterOutlet, ɵEmptyOutletComponent as _EmptyOutletComponent } from './_router-chunk.mjs';
import { Subject, of, from } from 'rxjs';
import { mergeAll, catchError, filter, concatMap, mergeMap } from 'rxjs/operators';
class ReactiveRouterState {
router = inject(Router);
stateManager = inject(StateManager);
fragment = signal('', ...(ngDevMode ? [{
debugName: "fragment"
}] : []));
queryParams = signal({}, ...(ngDevMode ? [{
debugName: "queryParams"
}] : []));
path = signal('', ...(ngDevMode ? [{
debugName: "path"
}] : []));
serializer = inject(UrlSerializer);
constructor() {
this.updateState();
this.router.events?.subscribe(e => {
if (e instanceof NavigationEnd) {
this.updateState();
}
});
}
updateState() {
const {
fragment,
root,
queryParams
} = this.stateManager.getCurrentUrlTree();
this.fragment.set(fragment);
this.queryParams.set(queryParams);
this.path.set(this.serializer.serialize(new UrlTree(root)));
}
static ɵfac = i0.ɵɵngDeclareFactory({
minVersion: "12.0.0",
version: "21.2.0",
ngImport: i0,
type: ReactiveRouterState,
deps: [],
target: i0.ɵɵFactoryTarget.Injectable
});
static ɵprov = i0.ɵɵngDeclareInjectable({
minVersion: "12.0.0",
version: "21.2.0",
ngImport: i0,
type: ReactiveRouterState,
providedIn: 'root'
});
}
i0.ɵɵngDeclareClassMetadata({
minVersion: "12.0.0",
version: "21.2.0",
ngImport: i0,
type: ReactiveRouterState,
decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}],
ctorParameters: () => []
});
class RouterLink {
router;
route;
tabIndexAttribute;
renderer;
el;
locationStrategy;
hrefAttributeValue = inject(new HostAttributeToken('href'), {
optional: true
});
reactiveHref = linkedSignal(() => {
if (!this.isAnchorElement) {
return this.hrefAttributeValue;
}
return this.computeHref(this._urlTree());
}, ...(ngDevMode ? [{
debugName: "reactiveHref"
}] : []));
get href() {
return untracked(this.reactiveHref);
}
set href(value) {
this.reactiveHref.set(value);
}
set target(value) {
this._target.set(value);
}
get target() {
return untracked(this._target);
}
_target = signal(undefined, ...(ngDevMode ? [{
debugName: "_target"
}] : []));
set queryParams(value) {
this._queryParams.set(value);
}
get queryParams() {
return untracked(this._queryParams);
}
_queryParams = signal(undefined, {
...(ngDevMode ? {
debugName: "_queryParams"
} : {}),
equal: () => false
});
set fragment(value) {
this._fragment.set(value);
}
get fragment() {
return untracked(this._fragment);
}
_fragment = signal(undefined, ...(ngDevMode ? [{
debugName: "_fragment"
}] : []));
set queryParamsHandling(value) {
this._queryParamsHandling.set(value);
}
get queryParamsHandling() {
return untracked(this._queryParamsHandling);
}
_queryParamsHandling = signal(undefined, ...(ngDevMode ? [{
debugName: "_queryParamsHandling"
}] : []));
set state(value) {
this._state.set(value);
}
get state() {
return untracked(this._state);
}
_state = signal(undefined, {
...(ngDevMode ? {
debugName: "_state"
} : {}),
equal: () => false
});
set info(value) {
this._info.set(value);
}
get info() {
return untracked(this._info);
}
_info = signal(undefined, {
...(ngDevMode ? {
debugName: "_info"
} : {}),
equal: () => false
});
set relativeTo(value) {
this._relativeTo.set(value);
}
get relativeTo() {
return untracked(this._relativeTo);
}
_relativeTo = signal(undefined, ...(ngDevMode ? [{
debugName: "_relativeTo"
}] : []));
set preserveFragment(value) {
this._preserveFragment.set(value);
}
get preserveFragment() {
return untracked(this._preserveFragment);
}
_preserveFragment = signal(false, ...(ngDevMode ? [{
debugName: "_preserveFragment"
}] : []));
set skipLocationChange(value) {
this._skipLocationChange.set(value);
}
get skipLocationChange() {
return untracked(this._skipLocationChange);
}
_skipLocationChange = signal(false, ...(ngDevMode ? [{
debugName: "_skipLocationChange"
}] : []));
set replaceUrl(value) {
this._replaceUrl.set(value);
}
get replaceUrl() {
return untracked(this._replaceUrl);
}
_replaceUrl = signal(false, ...(ngDevMode ? [{
debugName: "_replaceUrl"
}] : []));
isAnchorElement;
onChanges = new Subject();
applicationErrorHandler = inject(_INTERNAL_APPLICATION_ERROR_HANDLER);
options = inject(ROUTER_CONFIGURATION, {
optional: true
});
reactiveRouterState = inject(ReactiveRouterState);
constructor(router, route, tabIndexAttribute, renderer, el, locationStrategy) {
this.router = router;
this.route = route;
this.tabIndexAttribute = tabIndexAttribute;
this.renderer = renderer;
this.el = el;
this.locationStrategy = locationStrategy;
const tagName = el.nativeElement.tagName?.toLowerCase();
this.isAnchorElement = tagName === 'a' || tagName === 'area' || !!(typeof customElements === 'object' && customElements.get(tagName)?.observedAttributes?.includes?.('href'));
if (typeof ngDevMode !== 'undefined' && ngDevMode) {
effect(() => {
if (isUrlTree(this.routerLinkInput()) && (this._fragment() !== undefined || this._queryParams() || this._queryParamsHandling() || this._preserveFragment() || this._relativeTo())) {
throw new _RuntimeError(4017, 'Cannot configure queryParams or fragment when using a UrlTree as the routerLink input value.');
}
});
}
}
setTabIndexIfNotOnNativeEl(newTabIndex) {
if (this.tabIndexAttribute != null || this.isAnchorElement) {
return;
}
this.applyAttributeValue('tabindex', newTabIndex);
}
ngOnChanges(changes) {
this.onChanges.next(this);
}
routerLinkInput = signal(null, ...(ngDevMode ? [{
debugName: "routerLinkInput"
}] : []));
set routerLink(commandsOrUrlTree) {
if (commandsOrUrlTree == null) {
this.routerLinkInput.set(null);
this.setTabIndexIfNotOnNativeEl(null);
} else {
if (isUrlTree(commandsOrUrlTree)) {
this.routerLinkInput.set(commandsOrUrlTree);
} else {
this.routerLinkInput.set(Array.isArray(commandsOrUrlTree) ? commandsOrUrlTree : [commandsOrUrlTree]);
}
this.setTabIndexIfNotOnNativeEl('0');
}
}
onClick(button, ctrlKey, shiftKey, altKey, metaKey) {
const urlTree = this._urlTree();
if (urlTree === null) {
return true;
}
if (this.isAnchorElement) {
if (button !== 0 || ctrlKey || shiftKey || altKey || metaKey) {
return true;
}
if (typeof this.target === 'string' && this.target != '_self') {
return true;
}
}
const extras = {
skipLocationChange: this.skipLocationChange,
replaceUrl: this.replaceUrl,
state: this.state,
info: this.info
};
this.router.navigateByUrl(urlTree, extras)?.catch(e => {
this.applicationErrorHandler(e);
});
return !this.isAnchorElement;
}
ngOnDestroy() {}
applyAttributeValue(attrName, attrValue) {
const renderer = this.renderer;
const nativeElement = this.el.nativeElement;
if (attrValue !== null) {
renderer.setAttribute(nativeElement, attrName, attrValue);
} else {
renderer.removeAttribute(nativeElement, attrName);
}
}
_urlTree = computed(() => {
this.reactiveRouterState.path();
if (this._preserveFragment()) {
this.reactiveRouterState.fragment();
}
const shouldTrackParams = handling => handling === 'preserve' || handling === 'merge';
if (shouldTrackParams(this._queryParamsHandling()) || shouldTrackParams(this.options?.defaultQueryParamsHandling)) {
this.reactiveRouterState.queryParams();
}
const routerLinkInput = this.routerLinkInput();
if (routerLinkInput === null || !this.router.createUrlTree) {
return null;
} else if (isUrlTree(routerLinkInput)) {
return routerLinkInput;
}
return this.router.createUrlTree(routerLinkInput, {
relativeTo: this._relativeTo() !== undefined ? this._relativeTo() : this.route,
queryParams: this._queryParams(),
fragment: this._fragment(),
queryParamsHandling: this._queryParamsHandling(),
preserveFragment: this._preserveFragment()
});
}, {
...(ngDevMode ? {
debugName: "_urlTree"
} : {}),
equal: (a, b) => this.computeHref(a) === this.computeHref(b)
});
get urlTree() {
return untracked(this._urlTree);
}
computeHref(urlTree) {
return urlTree !== null && this.locationStrategy ? this.locationStrategy?.prepareExternalUrl(this.router.serializeUrl(urlTree)) ?? '' : null;
}
static ɵfac = i0.ɵɵngDeclareFactory({
minVersion: "12.0.0",
version: "21.2.0",
ngImport: i0,
type: RouterLink,
deps: [{
token: Router
}, {
token: ActivatedRoute
}, {
token: 'tabindex',
attribute: true
}, {
token: i0.Renderer2
}, {
token: i0.ElementRef
}, {
token: i3.LocationStrategy
}],
target: i0.ɵɵFactoryTarget.Directive
});
static ɵdir = i0.ɵɵngDeclareDirective({
minVersion: "16.1.0",
version: "21.2.0",
type: RouterLink,
isStandalone: true,
selector: "[routerLink]",
inputs: {
target: "target",
queryParams: "queryParams",
fragment: "fragment",
queryParamsHandling: "queryParamsHandling",
state: "state",
info: "info",
relativeTo: "relativeTo",
preserveFragment: ["preserveFragment", "preserveFragment", booleanAttribute],
skipLocationChange: ["skipLocationChange", "skipLocationChange", booleanAttribute],
replaceUrl: ["replaceUrl", "replaceUrl", booleanAttribute],
routerLink: "routerLink"
},
host: {
listeners: {
"click": "onClick($event.button,$event.ctrlKey,$event.shiftKey,$event.altKey,$event.metaKey)"
},
properties: {
"attr.href": "reactiveHref()",
"attr.target": "_target()"
}
},
usesOnChanges: true,
ngImport: i0
});
}
i0.ɵɵngDeclareClassMetadata({
minVersion: "12.0.0",
version: "21.2.0",
ngImport: i0,
type: RouterLink,
decorators: [{
type: Directive,
args: [{
selector: '[routerLink]',
host: {
'[attr.href]': 'reactiveHref()',
'[attr.target]': '_target()'
}
}]
}],
ctorParameters: () => [{
type: Router
}, {
type: ActivatedRoute
}, {
type: undefined,
decorators: [{
type: Attribute,
args: ['tabindex']
}]
}, {
type: i0.Renderer2
}, {
type: i0.ElementRef
}, {
type: i3.LocationStrategy
}],
propDecorators: {
target: [{
type: Input
}],
queryParams: [{
type: Input
}],
fragment: [{
type: Input
}],
queryParamsHandling: [{
type: Input
}],
state: [{
type: Input
}],
info: [{
type: Input
}],
relativeTo: [{
type: Input
}],
preserveFragment: [{
type: Input,
args: [{
transform: booleanAttribute
}]
}],
skipLocationChange: [{
type: Input,
args: [{
transform: booleanAttribute
}]
}],
replaceUrl: [{
type: Input,
args: [{
transform: booleanAttribute
}]
}],
routerLink: [{
type: Input
}],
onClick: [{
type: HostListener,
args: ['click', ['$event.button', '$event.ctrlKey', '$event.shiftKey', '$event.altKey', '$event.metaKey']]
}]
}
});
class RouterLinkActive {
router;
element;
renderer;
cdr;
links;
classes = [];
routerEventsSubscription;
linkInputChangesSubscription;
_isActive = false;
get isActive() {
return this._isActive;
}
routerLinkActiveOptions = {
exact: false
};
ariaCurrentWhenActive;
isActiveChange = new EventEmitter();
link = inject(RouterLink, {
optional: true
});
constructor(router, element, renderer, cdr) {
this.router = router;
this.element = element;
this.renderer = renderer;
this.cdr = cdr;
this.routerEventsSubscription = router.events.subscribe(s => {
if (s instanceof NavigationEnd) {
this.update();
}
});
}
ngAfterContentInit() {
of(this.links.changes, of(null)).pipe(mergeAll()).subscribe(_ => {
this.update();
this.subscribeToEachLinkOnChanges();
});
}
subscribeToEachLinkOnChanges() {
this.linkInputChangesSubscription?.unsubscribe();
const allLinkChanges = [...this.links.toArray(), this.link].filter(link => !!link).map(link => link.onChanges);
this.linkInputChangesSubscription = from(allLinkChanges).pipe(mergeAll()).subscribe(link => {
if (this._isActive !== this.isLinkActive(this.router)(link)) {
this.update();
}
});
}
set routerLinkActive(data) {
const classes = Array.isArray(data) ? data : data.split(' ');
this.classes = classes.filter(c => !!c);
}
ngOnChanges(changes) {
this.update();
}
ngOnDestroy() {
this.routerEventsSubscription.unsubscribe();
this.linkInputChangesSubscription?.unsubscribe();
}
update() {
if (!this.links || !this.router.navigated) return;
queueMicrotask(() => {
const hasActiveLinks = this.hasActiveLinks();
this.classes.forEach(c => {
if (hasActiveLinks) {
this.renderer.addClass(this.element.nativeElement, c);
} else {
this.renderer.removeClass(this.element.nativeElement, c);
}
});
if (hasActiveLinks && this.ariaCurrentWhenActive !== undefined) {
this.renderer.setAttribute(this.element.nativeElement, 'aria-current', this.ariaCurrentWhenActive.toString());
} else {
this.renderer.removeAttribute(this.element.nativeElement, 'aria-current');
}
if (this._isActive !== hasActiveLinks) {
this._isActive = hasActiveLinks;
this.cdr.markForCheck();
this.isActiveChange.emit(hasActiveLinks);
}
});
}
isLinkActive(router) {
const options = isActiveMatchOptions(this.routerLinkActiveOptions) ? this.routerLinkActiveOptions : this.routerLinkActiveOptions.exact ?? false ? {
...exactMatchOptions
} : {
...subsetMatchOptions
};
return link => {
const urlTree = link.urlTree;
return urlTree ? untracked(isActive(urlTree, router, options)) : false;
};
}
hasActiveLinks() {
const isActiveCheckFn = this.isLinkActive(this.router);
return this.link && isActiveCheckFn(this.link) || this.links.some(isActiveCheckFn);
}
static ɵfac = i0.ɵɵngDeclareFactory({
minVersion: "12.0.0",
version: "21.2.0",
ngImport: i0,
type: RouterLinkActive,
deps: [{
token: Router
}, {
token: i0.ElementRef
}, {
token: i0.Renderer2
}, {
token: i0.ChangeDetectorRef
}],
target: i0.ɵɵFactoryTarget.Directive
});
static ɵdir = i0.ɵɵngDeclareDirective({
minVersion: "14.0.0",
version: "21.2.0",
type: RouterLinkActive,
isStandalone: true,
selector: "[routerLinkActive]",
inputs: {
routerLinkActiveOptions: "routerLinkActiveOptions",
ariaCurrentWhenActive: "ariaCurrentWhenActive",
routerLinkActive: "routerLinkActive"
},
outputs: {
isActiveChange: "isActiveChange"
},
queries: [{
propertyName: "links",
predicate: RouterLink,
descendants: true
}],
exportAs: ["routerLinkActive"],
usesOnChanges: true,
ngImport: i0
});
}
i0.ɵɵngDeclareClassMetadata({
minVersion: "12.0.0",
version: "21.2.0",
ngImport: i0,
type: RouterLinkActive,
decorators: [{
type: Directive,
args: [{
selector: '[routerLinkActive]',
exportAs: 'routerLinkActive'
}]
}],
ctorParameters: () => [{
type: Router
}, {
type: i0.ElementRef
}, {
type: i0.Renderer2
}, {
type: i0.ChangeDetectorRef
}],
propDecorators: {
links: [{
type: ContentChildren,
args: [RouterLink, {
descendants: true
}]
}],
routerLinkActiveOptions: [{
type: Input
}],
ariaCurrentWhenActive: [{
type: Input
}],
isActiveChange: [{
type: Output
}],
routerLinkActive: [{
type: Input
}]
}
});
function isActiveMatchOptions(options) {
const o = options;
return !!(o.paths || o.matrixParams || o.queryParams || o.fragment);
}
class PreloadingStrategy {}
class PreloadAllModules {
preload(route, fn) {
return fn().pipe(catchError(() => of(null)));
}
static ɵfac = i0.ɵɵngDeclareFactory({
minVersion: "12.0.0",
version: "21.2.0",
ngImport: i0,
type: PreloadAllModules,
deps: [],
target: i0.ɵɵFactoryTarget.Injectable
});
static ɵprov = i0.ɵɵngDeclareInjectable({
minVersion: "12.0.0",
version: "21.2.0",
ngImport: i0,
type: PreloadAllModules,
providedIn: 'root'
});
}
i0.ɵɵngDeclareClassMetadata({
minVersion: "12.0.0",
version: "21.2.0",
ngImport: i0,
type: PreloadAllModules,
decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}]
});
class NoPreloading {
preload(route, fn) {
return of(null);
}
static ɵfac = i0.ɵɵngDeclareFactory({
minVersion: "12.0.0",
version: "21.2.0",
ngImport: i0,
type: NoPreloading,
deps: [],
target: i0.ɵɵFactoryTarget.Injectable
});
static ɵprov = i0.ɵɵngDeclareInjectable({
minVersion: "12.0.0",
version: "21.2.0",
ngImport: i0,
type: NoPreloading,
providedIn: 'root'
});
}
i0.ɵɵngDeclareClassMetadata({
minVersion: "12.0.0",
version: "21.2.0",
ngImport: i0,
type: NoPreloading,
decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}]
});
class RouterPreloader {
router;
injector;
preloadingStrategy;
loader;
subscription;
constructor(router, injector, preloadingStrategy, loader) {
this.router = router;
this.injector = injector;
this.preloadingStrategy = preloadingStrategy;
this.loader = loader;
}
setUpPreloading() {
this.subscription = this.router.events.pipe(filter(e => e instanceof NavigationEnd), concatMap(() => this.preload())).subscribe(() => {});
}
preload() {
return this.processRoutes(this.injector, this.router.config);
}
ngOnDestroy() {
this.subscription?.unsubscribe();
}
processRoutes(injector, routes) {
const res = [];
for (const route of routes) {
if (route.providers && !route._injector) {
route._injector = createEnvironmentInjector(route.providers, injector, typeof ngDevMode === 'undefined' || ngDevMode ? `Route: ${route.path}` : '');
}
const injectorForCurrentRoute = route._injector ?? injector;
if (route._loadedNgModuleFactory && !route._loadedInjector) {
route._loadedInjector = route._loadedNgModuleFactory.create(injectorForCurrentRoute).injector;
}
const injectorForChildren = route._loadedInjector ?? injectorForCurrentRoute;
if (route.loadChildren && !route._loadedRoutes && route.canLoad === undefined || route.loadComponent && !route._loadedComponent) {
res.push(this.preloadConfig(injectorForCurrentRoute, route));
}
if (route.children || route._loadedRoutes) {
res.push(this.processRoutes(injectorForChildren, route.children ?? route._loadedRoutes));
}
}
return from(res).pipe(mergeAll());
}
preloadConfig(injector, route) {
return this.preloadingStrategy.preload(route, () => {
if (injector.destroyed) {
return of(null);
}
let loadedChildren$;
if (route.loadChildren && route.canLoad === undefined) {
loadedChildren$ = from(this.loader.loadChildren(injector, route));
} else {
loadedChildren$ = of(null);
}
const recursiveLoadChildren$ = loadedChildren$.pipe(mergeMap(config => {
if (config === null) {
return of(void 0);
}
route._loadedRoutes = config.routes;
route._loadedInjector = config.injector;
route._loadedNgModuleFactory = config.factory;
return this.processRoutes(config.injector ?? injector, config.routes);
}));
if (route.loadComponent && !route._loadedComponent) {
const loadComponent$ = this.loader.loadComponent(injector, route);
return from([recursiveLoadChildren$, loadComponent$]).pipe(mergeAll());
} else {
return recursiveLoadChildren$;
}
});
}
static ɵfac = i0.ɵɵngDeclareFactory({
minVersion: "12.0.0",
version: "21.2.0",
ngImport: i0,
type: RouterPreloader,
deps: [{
token: Router
}, {
token: i0.EnvironmentInjector
}, {
token: PreloadingStrategy
}, {
token: RouterConfigLoader
}],
target: i0.ɵɵFactoryTarget.Injectable
});
static ɵprov = i0.ɵɵngDeclareInjectable({
minVersion: "12.0.0",
version: "21.2.0",
ngImport: i0,
type: RouterPreloader,
providedIn: 'root'
});
}
i0.ɵɵngDeclareClassMetadata({
minVersion: "12.0.0",
version: "21.2.0",
ngImport: i0,
type: RouterPreloader,
decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}],
ctorParameters: () => [{
type: Router
}, {
type: i0.EnvironmentInjector
}, {
type: PreloadingStrategy
}, {
type: RouterConfigLoader
}]
});
const ROUTER_SCROLLER = new InjectionToken(typeof ngDevMode !== 'undefined' && ngDevMode ? 'Router Scroller' : '');
class RouterScroller {
options;
routerEventsSubscription;
scrollEventsSubscription;
lastId = 0;
lastSource = IMPERATIVE_NAVIGATION;
restoredId = 0;
store = {};
urlSerializer = inject(UrlSerializer);
zone = inject(NgZone);
viewportScroller = inject(ViewportScroller);
transitions = inject(NavigationTransitions);
constructor(options) {
this.options = options;
this.options.scrollPositionRestoration ||= 'disabled';
this.options.anchorScrolling ||= 'disabled';
}
init() {
if (this.options.scrollPositionRestoration !== 'disabled') {
this.viewportScroller.setHistoryScrollRestoration('manual');
}
this.routerEventsSubscription = this.createScrollEvents();
this.scrollEventsSubscription = this.consumeScrollEvents();
}
createScrollEvents() {
return this.transitions.events.subscribe(e => {
if (e instanceof NavigationStart) {
this.store[this.lastId] = this.viewportScroller.getScrollPosition();
this.lastSource = e.navigationTrigger;
this.restoredId = e.restoredState ? e.restoredState.navigationId : 0;
} else if (e instanceof NavigationEnd) {
this.lastId = e.id;
this.scheduleScrollEvent(e, this.urlSerializer.parse(e.urlAfterRedirects).fragment);
} else if (e instanceof NavigationSkipped && e.code === NavigationSkippedCode.IgnoredSameUrlNavigation) {
this.lastSource = undefined;
this.restoredId = 0;
this.scheduleScrollEvent(e, this.urlSerializer.parse(e.url).fragment);
}
});
}
consumeScrollEvents() {
return this.transitions.events.subscribe(e => {
if (!(e instanceof Scroll) || e.scrollBehavior === 'manual') return;
const instantScroll = {
behavior: 'instant'
};
if (e.position) {
if (this.options.scrollPositionRestoration === 'top') {
this.viewportScroller.scrollToPosition([0, 0], instantScroll);
} else if (this.options.scrollPositionRestoration === 'enabled') {
this.viewportScroller.scrollToPosition(e.position, instantScroll);
}
} else {
if (e.anchor && this.options.anchorScrolling === 'enabled') {
this.viewportScroller.scrollToAnchor(e.anchor);
} else if (this.options.scrollPositionRestoration !== 'disabled') {
this.viewportScroller.scrollToPosition([0, 0]);
}
}
});
}
scheduleScrollEvent(routerEvent, anchor) {
const scroll = untracked(this.transitions.currentNavigation)?.extras.scroll;
this.zone.runOutsideAngular(async () => {
await new Promise(resolve => {
setTimeout(resolve);
if (typeof requestAnimationFrame !== 'undefined') {
requestAnimationFrame(resolve);
}
});
this.zone.run(() => {
this.transitions.events.next(new Scroll(routerEvent, this.lastSource === 'popstate' ? this.store[this.restoredId] : null, anchor, scroll));
});
});
}
ngOnDestroy() {
this.routerEventsSubscription?.unsubscribe();
this.scrollEventsSubscription?.unsubscribe();
}
static ɵfac = i0.ɵɵngDeclareFactory({
minVersion: "12.0.0",
version: "21.2.0",
ngImport: i0,
type: RouterScroller,
deps: "invalid",
target: i0.ɵɵFactoryTarget.Injectable
});
static ɵprov = i0.ɵɵngDeclareInjectable({
minVersion: "12.0.0",
version: "21.2.0",
ngImport: i0,
type: RouterScroller
});
}
i0.ɵɵngDeclareClassMetadata({
minVersion: "12.0.0",
version: "21.2.0",
ngImport: i0,
type: RouterScroller,
decorators: [{
type: Injectable
}],
ctorParameters: () => [{
type: undefined
}]
});
function getLoadedRoutes(route) {
return route._loadedRoutes;
}
function getRouterInstance(injector) {
return injector.get(Router, null, {
optional: true
});
}
function navigateByUrl(router, url) {
if (!(router instanceof Router)) {
throw new Error('The provided router is not an Angular Router.');
}
return router.navigateByUrl(url);
}
class NavigationStateManager extends StateManager {
injector = inject(EnvironmentInjector);
navigation = inject(PlatformNavigation);
inMemoryScrollingEnabled = inject(ROUTER_SCROLLER, {
optional: true
}) !== null;
base = new URL(inject(PlatformLocation).href).origin;
appRootURL = new URL(this.location.prepareExternalUrl?.('/') ?? '/', this.base).href;
precommitHandlerSupported = inject(_PRECOMMIT_HANDLER_SUPPORTED);
activeHistoryEntry = this.navigation.currentEntry;
currentNavigation = {};
nonRouterCurrentEntryChangeSubject = new Subject();
nonRouterEntryChangeListener;
get registered() {
return this.nonRouterEntryChangeListener !== undefined && !this.nonRouterEntryChangeListener.closed;
}
constructor() {
super();
const navigateListener = event => {
this.handleNavigate(event);
};
this.navigation.addEventListener('navigate', navigateListener);
inject(DestroyRef).onDestroy(() => this.navigation.removeEventListener('navigate', navigateListener));
}
registerNonRouterCurrentEntryChangeListener(listener) {
this.activeHistoryEntry = this.navigation.currentEntry;
this.nonRouterEntryChangeListener = this.nonRouterCurrentEntryChangeSubject.subscribe(({
path,
state
}) => {
listener(path, state, 'popstate', !this.precommitHandlerSupported ? {
replaceUrl: true
} : {});
});
return this.nonRouterEntryChangeListener;
}
async handleRouterEvent(e, transition) {
this.currentNavigation = {
...this.currentNavigation,
routerTransition: transition
};
if (e instanceof NavigationStart) {
this.updateStateMemento();
if (this.precommitHandlerSupported) {
this.maybeCreateNavigationForTransition(transition);
}
} else if (e instanceof NavigationSkipped) {
this.finishNavigation();
this.commitTransition(transition);
} else if (e instanceof BeforeRoutesRecognized) {
transition.routesRecognizeHandler.deferredHandle = new Promise(async resolve => {
if (this.urlUpdateStrategy === 'eager') {
try {
this.maybeCreateNavigationForTransition(transition);
await this.currentNavigation.commitUrl?.();
} catch {
return;
}
}
resolve();
});
} else if (e instanceof BeforeActivateRoutes) {
transition.beforeActivateHandler.deferredHandle = new Promise(async resolve => {
if (this.urlUpdateStrategy === 'deferred') {
try {
this.maybeCreateNavigationForTransition(transition);
await this.currentNavigation.commitUrl?.();
} catch {
return;
}
}
this.commitTransition(transition);
resolve();
});
} else if (e instanceof NavigationCancel || e instanceof NavigationError) {
const redirectingBeforeUrlCommit = e instanceof NavigationCancel && e.code === NavigationCancellationCode.Redirect && !!this.currentNavigation.commitUrl;
if (redirectingBeforeUrlCommit) {
return;
}
void this.cancel(transition, e);
} else if (e instanceof NavigationEnd) {
const {
resolveHandler,
removeAbortListener
} = this.currentNavigation;
this.currentNavigation = {};
removeAbortListener?.();
this.activeHistoryEntry = this.navigation.currentEntry;
afterNextRender({
read: () => resolveHandler?.()
}, {
injector: this.injector
});
}
}
maybeCreateNavigationForTransition(transition) {
const {
navigationEvent,
commitUrl
} = this.currentNavigation;
if (commitUrl || navigationEvent && navigationEvent.navigationType === 'traverse' && this.eventAndRouterDestinationsMatch(navigationEvent, transition)) {
return;
}
this.currentNavigation.removeAbortListener?.();
const path = this.createBrowserPath(transition);
this.navigate(path, transition);
}
navigate(internalPath, transition) {
const path = transition.extras.skipLocationChange ? this.navigation.currentEntry.url : this.location.prepareExternalUrl(internalPath);
const state = {
...transition.extras.state,
navigationId: transition.id
};
const info = {
ɵrouterInfo: {
intercept: true
}
};
if (!this.navigation.transition && this.currentNavigation.navigationEvent) {
transition.extras.replaceUrl = false;
}
const history = this.location.isCurrentPathEqualTo(path) || transition.extras.replaceUrl || transition.extras.skipLocationChange ? 'replace' : 'push';
handleResultRejections(this.navigation.navigate(path, {
state,
history,
info
}));
}
finishNavigation() {
this.currentNavigation.commitUrl?.();
this.currentNavigation?.resolveHandler?.();
this.currentNavigation = {};
}
async cancel(transition, cause) {
this.currentNavigation.rejectNavigateEvent?.();
const clearedState = {};
this.currentNavigation = clearedState;
if (isRedirectingEvent(cause)) {
return;
}
const isTraversalReset = this.canceledNavigationResolution === 'computed' && this.navigation.currentEntry.key !== this.activeHistoryEntry.key;
this.resetInternalState(transition.finalUrl, isTraversalReset);
if (this.navigation.currentEntry.id === this.activeHistoryEntry.id) {
return;
}
if (cause instanceof NavigationCancel && cause.code === NavigationCancellationCode.Aborted) {
await Promise.resolve();
if (this.currentNavigation !== clearedState) {
return;
}
}
if (isTraversalReset) {
handleResultRejections(this.navigation.traverseTo(this.activeHistoryEntry.key, {
info: {
ɵrouterInfo: {
intercept: false
}
}
}));
} else {
const internalPath = this.urlSerializer.serialize(this.getCurrentUrlTree());
const pathOrUrl = this.location.prepareExternalUrl(internalPath);
handleResultRejections(this.navigation.navigate(pathOrUrl, {
state: this.activeHistoryEntry.getState(),
history: 'replace',
info: {
ɵrouterInfo: {
intercept: false
}
}
}));
}
}
resetInternalState(finalUrl, traversalReset) {
this.routerState = this.stateMemento.routerState;
this.currentUrlTree = this.stateMemento.currentUrlTree;
this.rawUrlTree = traversalReset ? this.stateMemento.rawUrlTree : this.urlHandlingStrategy.merge(this.currentUrlTree, finalUrl ?? this.rawUrlTree);
}
handleNavigate(event) {
if (!event.canIntercept || event.navigationType === 'reload') {
return;
}
const routerInfo = event?.info?.ɵrouterInfo;
if (routerInfo && !routerInfo.intercept) {
return;
}
const isTriggeredByRouterTransition = !!routerInfo;
if (!isTriggeredByRouterTransition) {
this.currentNavigation.routerTransition?.abort();
if (!this.registered) {
this.finishNavigation();
return;
}
}
this.currentNavigation = {
...this.currentNavigation
};
this.currentNavigation.navigationEvent = event;
const abortHandler = () => {
this.currentNavigation.routerTransition?.abort();
};
event.signal.addEventListener('abort', abortHandler);
this.currentNavigation.removeAbortListener = () => event.signal.removeEventListener('abort', abortHandler);
let scroll = this.inMemoryScrollingEnabled ? 'manual' : this.currentNavigation.routerTransition?.extras.scroll ?? 'after-transition';
const interceptOptions = {
scroll
};
const {
promise: handlerPromise,
resolve: resolveHandler,
reject: rejectHandler
} = _promiseWithResolvers();
const {
promise: precommitHandlerPromise,
resolve: resolvePrecommitHandler,
reject: rejectPrecommitHandler
} = _promiseWithResolvers();
this.currentNavigation.rejectNavigateEvent = () => {
event.signal.removeEventListener('abort', abortHandler);
rejectPrecommitHandler();
rejectHandler();
};
this.currentNavigation.resolveHandler = () => {
this.currentNavigation.removeAbortListener?.();
resolveHandler();
};
handlerPromise.catch(() => {});
precommitHandlerPromise.catch(() => {});
interceptOptions.handler = () => handlerPromise;
if (this.deferredCommitSupported(event)) {
const redirect = new Promise(resolve => {
interceptOptions.precommitHandler = controller => {
resolve(controller.redirect.bind(controller));
return precommitHandlerPromise;
};
});
this.currentNavigation.commitUrl = async () => {
this.currentNavigation.commitUrl = undefined;
const transition = this.currentNavigation.routerTransition;
if (transition && !transition.extras.skipLocationChange) {
const internalPath = this.createBrowserPath(transition);
const history = this.location.isCurrentPathEqualTo(internalPath) || !!transition.extras.replaceUrl ? 'replace' : 'push';
const state = {
...transition.extras.state,
navigationId: transition.id
};
const pathOrUrl = this.location.prepareExternalUrl(internalPath);
(await redirect)(pathOrUrl, {
state,
history
});
}
resolvePrecommitHandler();
return await this.navigation.transition?.committed;
};
}
event.intercept(interceptOptions);
if (!isTriggeredByRouterTransition) {
this.handleNavigateEventTriggeredOutsideRouterAPIs(event);
}
}
handleNavigateEventTriggeredOutsideRouterAPIs(event) {
const path = event.destination.url.substring(this.appRootURL.length - 1);
const state = event.destination.getState();
this.nonRouterCurrentEntryChangeSubject.next({
path,
state
});
}
eventAndRouterDestinationsMatch(navigateEvent, transition) {
const internalPath = this.createBrowserPath(transition);
const eventDestination = new URL(navigateEvent.destination.url);
const routerDestination = this.location.prepareExternalUrl(internalPath);
return new URL(routerDestination, eventDestination.origin).href === eventDestination.href;
}
deferredCommitSupported(event) {
return this.precommitHandlerSupported && event.cancelable && event.navigationType !== 'traverse';
}
static ɵfac = i0.ɵɵngDeclareFactory({
minVersion: "12.0.0",
version: "21.2.0",
ngImport: i0,
type: NavigationStateManager,
deps: [],
target: i0.ɵɵFactoryTarget.Injectable
});
static ɵprov = i0.ɵɵngDeclareInjectable({
minVersion: "12.0.0",
version: "21.2.0",
ngImport: i0,
type: NavigationStateManager,
providedIn: 'root'
});
}
i0.ɵɵngDeclareClassMetadata({
minVersion: "12.0.0",
version: "21.2.0",
ngImport: i0,
type: NavigationStateManager,
decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}],
ctorParameters: () => []
});
function handleResultRejections(result) {
result.finished?.catch(() => {});
result.committed?.catch(() => {});
return result;
}
function provideRouter(routes, ...features) {
if (typeof ngDevMode === 'undefined' || ngDevMode) {
_publishExternalGlobalUtil('ɵgetLoadedRoutes', getLoadedRoutes);
_publishExternalGlobalUtil('ɵgetRouterInstance', getRouterInstance);
_publishExternalGlobalUtil('ɵnavigateByUrl', navigateByUrl);
}
return makeEnvironmentProviders([{
provide: ROUTES,
multi: true,
useValue: routes
}, typeof ngDevMode === 'undefined' || ngDevMode ? {
provide: ROUTER_IS_PROVIDED,
useValue: true
} : [], {
provide: ActivatedRoute,
useFactory: rootRoute
}, {
provide: APP_BOOTSTRAP_LISTENER,
multi: true,
useFactory: getBootstrapListener
}, features.map(feature => feature.ɵproviders)]);
}
function rootRoute() {
return inject(Router).routerState.root;
}
function routerFeature(kind, providers) {
return {
ɵkind: kind,
ɵproviders: providers
};
}
const ROUTER_IS_PROVIDED = new InjectionToken(typeof ngDevMode !== 'undefined' && ngDevMode ? 'Router is provided' : '', {
factory: () => false
});
const routerIsProvidedDevModeCheck = {
provide: ENVIRONMENT_INITIALIZER,
multi: true,
useFactory() {
return () => {
if (!inject(ROUTER_IS_PROVIDED)) {
console.warn('`provideRoutes` was called without `provideRouter` or `RouterModule.forRoot`. ' + 'This is likely a mistake.');
}
};
}
};
function provideRoutes(routes) {
return [{
provide: ROUTES,
multi: true,
useValue: routes
}, typeof ngDevMode === 'undefined' || ngDevMode ? routerIsProvidedDevModeCheck : []];
}
function withInMemoryScrolling(options = {}) {
const providers = [{
provide: ROUTER_SCROLLER,
useFactory: () => new RouterScroller(options)
}];
return routerFeature(4, providers);
}
function withExperimentalPlatformNavigation() {
const devModeLocationCheck = typeof ngDevMode === 'undefined' || ngDevMode ? [provideEnvironmentInitializer(() => {
const locationInstance = inject(Location);
if (!(locationInstance instanceof _NavigationAdapterForLocation)) {
const locationConstructorName = locationInstance.constructor.name;
let message = `'withExperimentalPlatformNavigation' provides a 'Location' implementation that ensures navigation APIs are consistently used.` + ` An instance of ${locationConstructorName} was found instead.`;
if (locationConstructorName === 'SpyLocation') {
message += ` One of 'RouterTestingModule' or 'provideLocationMocks' was likely used. 'withExperimentalPlatformNavigation' does not work with these because they override the Location implementation.`;
}
throw new Error(message);
}
})] : [];
const providers = [{
provide: StateManager,
useExisting: NavigationStateManager
}, {
provide: Location,
useClass: _NavigationAdapterForLocation
}, devModeLocationCheck];
return routerFeature(11, providers);
}
function getBootstrapListener() {
const injector = inject(Injector);
return bootstrappedComponentRef => {
const ref = injector.get(ApplicationRef);
if (bootstrappedComponentRef !== ref.components[0]) {
return;
}
const router = injector.get(Router);
const bootstrapDone = injector.get(BOOTSTRAP_DONE);
if (injector.get(INITIAL_NAVIGATION) === 1) {
router.initialNavigation();
}
injector.get(ROUTER_PRELOADER, null, {
optional: true
})?.setUpPreloading();
injector.get(ROUTER_SCROLLER, null, {
optional: true
})?.init();
router.resetRootComponentType(ref.componentTypes[0]);
if (!bootstrapDone.closed) {
bootstrapDone.next();
bootstrapDone.complete();
bootstrapDone.unsubscribe();
}
};
}
const BOOTSTRAP_DONE = new InjectionToken(typeof ngDevMode === 'undefined' || ngDevMode ? 'bootstrap done indicator' : '', {
factory: () => {
return new Subject();
}
});
const INITIAL_NAVIGATION = new InjectionToken(typeof ngDevMode === 'undefined' || ngDevMode ? 'initial navigation' : '', {
factory: () => 1
});
function withEnabledBlockingInitialNavigation() {
const providers = [{
provide: _IS_ENABLED_BLOCKING_INITIAL_NAVIGATION,
useValue: true
}, {
provide: INITIAL_NAVIGATION,
useValue: 0
}, provideAppInitializer(() => {
const injector = inject(Injector);
const locationInitialized = injector.get(LOCATION_INITIALIZED, Promise.resolve());
return locationInitialized.then(() => {
return new Promise(resolve => {
const router = injector.get(Router);
const bootstrapDone = injector.get(BOOTSTRAP_DONE);
afterNextNavigation(router, () => {
resolve(true);
});
injector.get(NavigationTransitions).afterPreactivation = () => {
resolve(true);
return bootstrapDone.closed ? of(void 0) : bootstrapDone;
};
router.initialNavigation();
});
});
})];
return routerFeature(2, providers);
}
function withDisabledInitialNavigation() {
const providers = [provideAppInitializer(() => {
inject(Router).setUpLocationChangeListener();
}), {
provide: INITIAL_NAVIGATION,
useValue: 2
}];
return routerFeature(3, providers);
}
function withDebugTracing() {
let providers = [];
if (typeof ngDevMode === 'undefined' || ngDevMode) {
providers = [{
provide: ENVIRONMENT_INITIALIZER,
multi: true,
useFactory: () => {
const router = inject(Router);
return () => router.events.subscribe(e => {
console.group?.(`Router Event: ${e.constructor.name}`);
console.log(stringifyEvent(e));
console.log(e);
console.groupEnd?.();
});
}
}];
} else {
providers = [];
}
return routerFeature(1, providers);
}
const ROUTER_PRELOADER = new InjectionToken(typeof ngDevMode === 'undefined' || ngDevMode ? 'router preloader' : '');
function withPreloading(preloadingStrategy) {
const providers = [{
provide: ROUTER_PRELOADER,
useExisting: RouterPreloader
}, {
provide: PreloadingStrategy,
useExisting: preloadingStrategy
}];
return routerFeature(0, providers);
}
function withRouterConfig(options) {
const providers = [{
provide: ROUTER_CONFIGURATION,
useValue: options
}];
return routerFeature(5, providers);
}
function withHashLocation() {
const providers = [{
provide: LocationStrategy,
useClass: HashLocationStrategy
}];
return routerFeature(6, providers);
}
function withNavigationErrorHandler(handler) {
const providers = [{
provide: NAVIGATION_ERROR_HANDLER,
useValue: handler
}];
return routerFeature(7, providers);
}
function withExperimentalAutoCleanupInjectors() {
return routerFeature(10, [{
provide: ROUTE_INJECTOR_CLEANUP,
useValue: routeInjectorCleanup
}]);
}
function withComponentInputBinding() {
const providers = [RoutedComponentInputBinder, {
provide: INPUT_BINDER,
useExisting: RoutedComponentInputBinder
}];
return routerFeature(8, providers);
}
function withViewTransitions(options) {
_performanceMarkFeature('NgRouterViewTransitions');
const providers = [{
provide: CREATE_VIEW_TRANSITION,
useValue: createViewTransition
}, {
provide: VIEW_TRANSITION_OPTIONS,
useValue: {
skipNextTransition: !!options?.skipInitialTransition,
...options
}
}];
return routerFeature(9, providers);
}
const ROUTER_DIRECTIVES = [RouterOutlet, RouterLink, RouterLinkActive, _EmptyOutletComponent];
const ROUTER_FORROOT_GUARD = new InjectionToken(typeof ngDevMode === 'undefined' || ngDevMode ? 'router duplicate forRoot guard' : '');
const ROUTER_PROVIDERS = [Location, {
provide: UrlSerializer,
useClass: DefaultUrlSerializer
}, Router, ChildrenOutletContexts, {
provide: ActivatedRoute,
useFactory: rootRoute
}, RouterConfigLoader, typeof ngDevMode === 'undefined' || ngDevMode ? {
provide: ROUTER_IS_PROVIDED,
useValue: true
} : []];
class RouterModule {
constructor() {
if (typeof ngDevMode === 'undefined' || ngDevMode) {
inject(ROUTER_FORROOT_GUARD, {
optional: true
});
}
}
static forRoot(routes, config) {
return {
ngModule: RouterModule,
providers: [ROUTER_PROVIDERS, typeof ngDevMode === 'undefined' || ngDevMode ? config?.enableTracing ? withDebugTracing().ɵproviders : [] : [], {
provide: ROUTES,
multi: true,
useValue: routes
}, typeof ngDevMode === 'undefined' || ngDevMode ? {
provide: ROUTER_FORROOT_GUARD,
useFactory: provideForRootGuard
} : [], config?.errorHandler ? {
provide: NAVIGATION_ERROR_HANDLER,
useValue: config.errorHandler
} : [], {
provide: ROUTER_CONFIGURATION,
useValue: config ? config : {}
}, config?.useHash ? provideHashLocationStrategy() : providePathLocationStrategy(), provideRouterScroller(), config?.preloadingStrategy ? withPreloading(config.preloadingStrategy).ɵproviders : [], config?.initialNavigation ? provideInitialNavigation(config) : [], config?.bindToComponentInputs ? withComponentInputBinding().ɵproviders : [], config?.enableViewTransitions ? withViewTransitions().ɵproviders : [], provideRouterInitializer()]
};
}
static forChild(routes) {
return {
ngModule: RouterModule,
providers: [{
provide: ROUTES,
multi: true,
useValue: routes
}]
};
}
static ɵfac = i0.ɵɵngDeclareFactory({
minVersion: "12.0.0",
version: "21.2.0",
ngImport: i0,
type: RouterModule,
deps: [],
target: i0.ɵɵFactoryTarget.NgModule
});
static ɵmod = i0.ɵɵngDeclareNgModule({
minVersion: "14.0.0",
version: "21.2.0",
ngImport: i0,
type: RouterModule,
imports: [RouterOutlet, RouterLink, RouterLinkActive, _EmptyOutletComponent],
exports: [RouterOutlet, RouterLink, RouterLinkActive, _EmptyOutletComponent]
});
static ɵinj = i0.ɵɵngDeclareInjector({
minVersion: "12.0.0",
version: "21.2.0",
ngImport: i0,
type: RouterModule
});
}
i0.ɵɵngDeclareClassMetadata({
minVersion: "12.0.0",
version: "21.2.0",
ngImport: i0,
type: RouterModule,
decorators: [{
type: NgModule,
args: [{
imports: ROUTER_DIRECTIVES,
exports: ROUTER_DIRECTIVES
}]
}],
ctorParameters: () => []
});
function provideRouterScroller() {
return {
provide: ROUTER_SCROLLER,
useFactory: () => {
const viewportScroller = inject(ViewportScroller);
const config = inject(ROUTER_CONFIGURATION);
if (config.scrollOffset) {
viewportScroller.setOffset(config.scrollOffset);
}
return new RouterScroller(config);
}
};
}
function provideHashLocationStrategy() {
return {
provide: LocationStrategy,
useClass: HashLocationStrategy
};
}
function providePathLocationStrategy() {
return {
provide: LocationStrategy,
useClass: PathLocationStrategy
};
}
function provideForRootGuard() {
const router = inject(Router, {
optional: true,
skipSelf: true
});
if (router) {
throw new _RuntimeError(4007, `The Router was pro