mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
refactor(router): Add provider for integrating with Navigation API and Location shim
This adds a (private) provider for integrating with the browser Navigation API. This provider ensures that interactions with the `Location` service use the underlying platform navigation rather than the history and location APIs.
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* @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 {Injectable, inject, DestroyRef} from '@angular/core';
|
||||
import {PlatformNavigation} from '../navigation/platform_navigation';
|
||||
import {Location} from './location';
|
||||
import {LocationStrategy} from './location_strategy';
|
||||
import {normalizeQueryParams} from './util';
|
||||
|
||||
/**
|
||||
* A `Location` implementation that uses the browser's `Navigation` API.
|
||||
*
|
||||
* This class is an adapter that maps the methods of the `Location` service to the newer
|
||||
* browser `Navigation` API. It is used when the `Navigation` API is available.
|
||||
*
|
||||
* This adapter uses `navigation.navigate()` for `go` and `replaceState` to ensure a single source
|
||||
* of truth for the navigation state. The Navigation API's state and `history.state` are separate.
|
||||
*
|
||||
* Note that `navigation.back()` and `navigation.forward()` can differ from the traditional
|
||||
* `history` API in how they traverse the joint session history.
|
||||
*
|
||||
* @see {@link Location}
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/Navigation_API
|
||||
*/
|
||||
@Injectable()
|
||||
export class NavigationAdapterForLocation extends Location {
|
||||
private readonly navigation = inject(PlatformNavigation);
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
|
||||
constructor() {
|
||||
super(inject(LocationStrategy));
|
||||
|
||||
this.registerNavigationListeners();
|
||||
}
|
||||
|
||||
private registerNavigationListeners() {
|
||||
const currentEntryChangeListener = () => {
|
||||
this._notifyUrlChangeListeners(this.path(true), this.getState());
|
||||
};
|
||||
this.navigation.addEventListener('currententrychange', currentEntryChangeListener);
|
||||
this.destroyRef.onDestroy(() => {
|
||||
this.navigation.removeEventListener('currententrychange', currentEntryChangeListener);
|
||||
});
|
||||
}
|
||||
|
||||
override getState(): unknown {
|
||||
return this.navigation.currentEntry?.getState();
|
||||
}
|
||||
|
||||
override replaceState(path: string, query: string = '', state: any = null): void {
|
||||
const url = this.prepareExternalUrl(path + normalizeQueryParams(query));
|
||||
// Use navigation API consistently for navigations. The "navigation API state"
|
||||
// field has no interaction with the existing "serialized state" field, which is what backs history.state
|
||||
this.navigation.navigate(url, {state, history: 'replace'});
|
||||
}
|
||||
|
||||
override go(path: string, query: string = '', state: any = null): void {
|
||||
const url = this.prepareExternalUrl(path + normalizeQueryParams(query));
|
||||
// Use navigation API consistently for navigations. The "navigation API state"
|
||||
// field has no interaction with the existing "serialized state" field, which is what backs history.state
|
||||
this.navigation.navigate(url, {state, history: 'push'});
|
||||
}
|
||||
|
||||
// Navigation.back/forward differs from history in how it traverses the joint session history
|
||||
// https://github.com/WICG/navigation-api?tab=readme-ov-file#correspondence-with-the-joint-session-history
|
||||
override back() {
|
||||
this.navigation.back();
|
||||
}
|
||||
|
||||
override forward() {
|
||||
this.navigation.forward();
|
||||
}
|
||||
|
||||
override onUrlChange(fn: (url: string, state: unknown) => void): VoidFunction {
|
||||
this._urlChangeListeners.push(fn);
|
||||
|
||||
return () => {
|
||||
const fnIndex = this._urlChangeListeners.indexOf(fn);
|
||||
this._urlChangeListeners.splice(fnIndex, 1);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -11,3 +11,4 @@ export {
|
||||
getDOM as ɵgetDOM,
|
||||
setRootDomAdapter as ɵsetRootDomAdapter,
|
||||
} from './dom_adapter';
|
||||
export {NavigationAdapterForLocation as ɵNavigationAdapterForLocation} from './location/navigation_adapter_for_location';
|
||||
|
||||
Reference in New Issue
Block a user