feat(common): add ability to watch for AngularJS URL updates through onUrlChange hook (#30466)

The LocationShim (replacement for `$location`) was added to centralize dealing with the browser URL. Additionally, an `onUrlChange` method was added to Angular's Location service. This PR adds a corresponding method to the LocationShim so updates from AngularJS can be tracked in Angular.

PR Close #30466
This commit is contained in:
Jason Aden
2019-05-14 15:00:21 -07:00
parent 077809398c
commit 1aff524b63
3 changed files with 116 additions and 0 deletions

View File

@ -39,9 +39,16 @@ export class $locationShim {
private $$search: any = '';
private $$hash: string = '';
private $$state: unknown;
private $$changeListeners: [
((url: string, state: unknown, oldUrl: string, oldState: unknown, err?: (e: Error) => void) =>
void),
(e: Error) => void
][] = [];
private cachedState: unknown = null;
constructor(
$injector: any, private location: Location, private platformLocation: PlatformLocation,
private urlCodec: UrlCodec, private locationStrategy: LocationStrategy) {
@ -313,6 +320,32 @@ export class $locationShim {
}
}
/**
* Register URL change listeners. This API can be used to catch updates performed by the
* AngularJS framework. These changes are a subset of the `$locationChangeStart/Success` events
* as those events fire when AngularJS updates it's internally referenced version of the browser
* URL. It's possible for `$locationChange` events to happen, but for the browser URL
* (window.location) to remain unchanged. This `onChange` callback will fire only when AngularJS
* actually updates the browser URL (window.location).
*/
onChange(
fn: (url: string, state: unknown, oldUrl: string, oldState: unknown) => void,
err: (e: Error) => void = (e: Error) => {}) {
this.$$changeListeners.push([fn, err]);
}
/** @internal */
$$notifyChangeListeners(
url: string = '', state: unknown, oldUrl: string = '', oldState: unknown) {
this.$$changeListeners.forEach(([fn, err]) => {
try {
fn(url, state, oldUrl, oldState);
} catch (e) {
err(e);
}
});
}
$$parse(url: string) {
let pathUrl: string|undefined;
if (url.startsWith('/')) {
@ -363,6 +396,7 @@ export class $locationShim {
// state object; this makes possible quick checking if the state changed in the digest
// loop. Checking deep equality would be too expensive.
this.$$state = this.browserState();
this.$$notifyChangeListeners(url, state, oldUrl, oldState);
} catch (e) {
// Restore old values if pushState fails
this.url(oldUrl);