fix(upgrade): update $locationShim to handle Location changes before initialization (#36498)

Updates the $locationShim to receive the most recent Location change
made, even if it happened before initialize() is called. This is
important when AngularJS bootstrapping is deferred and there is a delay
between when $locationShim is constructed and when it is initialized.
With this change, the $locationShim will correctly reflect any redirects
that occurred between construction and initialization.

Closes #36492

PR Close #36498
This commit is contained in:
Alison Gale 2020-04-07 16:44:28 -07:00 committed by atscott
parent 713bce19f4
commit 0cc53fb398
1 changed files with 28 additions and 12 deletions

View File

@ -8,6 +8,7 @@
import {Location, LocationStrategy, PlatformLocation} from '@angular/common'; import {Location, LocationStrategy, PlatformLocation} from '@angular/common';
import {UpgradeModule} from '@angular/upgrade/static'; import {UpgradeModule} from '@angular/upgrade/static';
import {ReplaySubject} from 'rxjs';
import {UrlCodec} from './params'; import {UrlCodec} from './params';
import {deepEqual, isAnchor, isPromise} from './utils'; import {deepEqual, isAnchor, isPromise} from './utils';
@ -50,7 +51,7 @@ export class $locationShim {
private cachedState: unknown = null; private cachedState: unknown = null;
private urlChanges = new ReplaySubject<{newUrl: string, newState: unknown}>(1);
constructor( constructor(
$injector: any, private location: Location, private platformLocation: PlatformLocation, $injector: any, private location: Location, private platformLocation: PlatformLocation,
@ -71,6 +72,10 @@ export class $locationShim {
this.cacheState(); this.cacheState();
this.$$state = this.browserState(); this.$$state = this.browserState();
this.location.onUrlChange((newUrl, newState) => {
this.urlChanges.next({newUrl, newState});
});
if (isPromise($injector)) { if (isPromise($injector)) {
$injector.then($i => this.initialize($i)); $injector.then($i => this.initialize($i));
} else { } else {
@ -88,7 +93,7 @@ export class $locationShim {
return; return;
} }
let elm: (Node & ParentNode)|null = event.target; let elm: (Node&ParentNode)|null = event.target;
// traverse the DOM up to find first A tag // traverse the DOM up to find first A tag
while (elm && elm.nodeName.toLowerCase() !== 'a') { while (elm && elm.nodeName.toLowerCase() !== 'a') {
@ -124,9 +129,9 @@ export class $locationShim {
} }
}); });
this.location.onUrlChange((newUrl, newState) => { this.urlChanges.subscribe(({newUrl, newState}) => {
let oldUrl = this.absUrl(); const oldUrl = this.absUrl();
let oldState = this.$$state; const oldState = this.$$state;
this.$$parse(newUrl); this.$$parse(newUrl);
newUrl = this.absUrl(); newUrl = this.absUrl();
this.$$state = newState; this.$$state = newState;
@ -286,7 +291,9 @@ export class $locationShim {
* This function emulates the $browser.state() function from AngularJS. It will cause * This function emulates the $browser.state() function from AngularJS. It will cause
* history.state to be cached unless changed with deep equality check. * history.state to be cached unless changed with deep equality check.
*/ */
private browserState(): unknown { return this.cachedState; } private browserState(): unknown {
return this.cachedState;
}
private stripBaseUrl(base: string, url: string) { private stripBaseUrl(base: string, url: string) {
if (url.startsWith(base)) { if (url.startsWith(base)) {
@ -446,7 +453,9 @@ export class $locationShim {
* // => "http://example.com/#/some/path?foo=bar&baz=xoxo" * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
* ``` * ```
*/ */
absUrl(): string { return this.$$absUrl; } absUrl(): string {
return this.$$absUrl;
}
/** /**
* Retrieves the current URL, or sets a new URL. When setting a URL, * Retrieves the current URL, or sets a new URL. When setting a URL,
@ -488,7 +497,9 @@ export class $locationShim {
* // => "http" * // => "http"
* ``` * ```
*/ */
protocol(): string { return this.$$protocol; } protocol(): string {
return this.$$protocol;
}
/** /**
* Retrieves the protocol of the current URL. * Retrieves the protocol of the current URL.
@ -509,7 +520,9 @@ export class $locationShim {
* // => "example.com:8080" * // => "example.com:8080"
* ``` * ```
*/ */
host(): string { return this.$$host; } host(): string {
return this.$$host;
}
/** /**
* Retrieves the port of the current URL. * Retrieves the port of the current URL.
@ -520,7 +533,9 @@ export class $locationShim {
* // => 80 * // => 80
* ``` * ```
*/ */
port(): number|null { return this.$$port; } port(): number|null {
return this.$$port;
}
/** /**
* Retrieves the path of the current URL, or changes the path and returns a reference to its own * Retrieves the path of the current URL, or changes the path and returns a reference to its own
@ -576,7 +591,8 @@ export class $locationShim {
* If the argument is a hash object containing an array of values, these values will be encoded * If the argument is a hash object containing an array of values, these values will be encoded
* as duplicate search parameters in the URL. * as duplicate search parameters in the URL.
* *
* @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue` * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number,
* then `paramValue`
* will override only a single search property. * will override only a single search property.
* *
* If `paramValue` is an array, it will override the property of the `search` component of * If `paramValue` is an array, it will override the property of the `search` component of