fix(router): location changes and redirects break the back button (#10742)
This commit is contained in:
parent
203b2ba637
commit
04c6b2fe85
|
@ -77,6 +77,7 @@ export class SpyLocation implements Location {
|
||||||
|
|
||||||
var url = path + (query.length > 0 ? ('?' + query) : '');
|
var url = path + (query.length > 0 ? ('?' + query) : '');
|
||||||
this.urlChanges.push(url);
|
this.urlChanges.push(url);
|
||||||
|
this._subject.emit({'url': url, 'pop': false});
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceState(path: string, query: string = '') {
|
replaceState(path: string, query: string = '') {
|
||||||
|
|
|
@ -47,6 +47,7 @@ export interface NavigationExtras {
|
||||||
preserveQueryParams?: boolean;
|
preserveQueryParams?: boolean;
|
||||||
preserveFragment?: boolean;
|
preserveFragment?: boolean;
|
||||||
skipLocationChange?: boolean;
|
skipLocationChange?: boolean;
|
||||||
|
replaceUrl?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -160,7 +161,7 @@ export class Router {
|
||||||
*/
|
*/
|
||||||
initialNavigation(): void {
|
initialNavigation(): void {
|
||||||
this.setUpLocationChangeListener();
|
this.setUpLocationChangeListener();
|
||||||
this.navigateByUrl(this.location.path(true));
|
this.navigateByUrl(this.location.path(true), {replaceUrl: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -336,7 +337,8 @@ export class Router {
|
||||||
private scheduleNavigation(url: UrlTree, extras: NavigationExtras): Promise<boolean> {
|
private scheduleNavigation(url: UrlTree, extras: NavigationExtras): Promise<boolean> {
|
||||||
const id = ++this.navigationId;
|
const id = ++this.navigationId;
|
||||||
this.routerEvents.next(new NavigationStart(id, this.serializeUrl(url)));
|
this.routerEvents.next(new NavigationStart(id, this.serializeUrl(url)));
|
||||||
return Promise.resolve().then((_) => this.runNavigate(url, extras.skipLocationChange, id));
|
return Promise.resolve().then(
|
||||||
|
(_) => this.runNavigate(url, extras.skipLocationChange, extras.replaceUrl, id));
|
||||||
}
|
}
|
||||||
|
|
||||||
private setUpLocationChangeListener(): void {
|
private setUpLocationChangeListener(): void {
|
||||||
|
@ -347,12 +349,14 @@ export class Router {
|
||||||
// we fire multiple events for a single URL change
|
// we fire multiple events for a single URL change
|
||||||
// we should navigate only once
|
// we should navigate only once
|
||||||
return this.currentUrlTree.toString() !== tree.toString() ?
|
return this.currentUrlTree.toString() !== tree.toString() ?
|
||||||
this.scheduleNavigation(tree, change['pop']) :
|
this.scheduleNavigation(tree, {skipLocationChange: change['pop'], replaceUrl: true}) :
|
||||||
null;
|
null;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private runNavigate(url: UrlTree, preventPushState: boolean, id: number): Promise<boolean> {
|
private runNavigate(
|
||||||
|
url: UrlTree, shouldPreventPushState: boolean, shouldReplaceUrl: boolean,
|
||||||
|
id: number): Promise<boolean> {
|
||||||
if (id !== this.navigationId) {
|
if (id !== this.navigationId) {
|
||||||
this.location.go(this.urlSerializer.serialize(this.currentUrlTree));
|
this.location.go(this.urlSerializer.serialize(this.currentUrlTree));
|
||||||
this.routerEvents.next(new NavigationCancel(id, this.serializeUrl(url)));
|
this.routerEvents.next(new NavigationCancel(id, this.serializeUrl(url)));
|
||||||
|
@ -415,9 +419,9 @@ export class Router {
|
||||||
|
|
||||||
new ActivateRoutes(state, storedState).activate(this.outletMap);
|
new ActivateRoutes(state, storedState).activate(this.outletMap);
|
||||||
|
|
||||||
if (!preventPushState) {
|
if (!shouldPreventPushState) {
|
||||||
let path = this.urlSerializer.serialize(appliedUrl);
|
let path = this.urlSerializer.serialize(appliedUrl);
|
||||||
if (this.location.isCurrentPathEqualTo(path)) {
|
if (this.location.isCurrentPathEqualTo(path) || shouldReplaceUrl) {
|
||||||
this.location.replaceState(path);
|
this.location.replaceState(path);
|
||||||
} else {
|
} else {
|
||||||
this.location.go(path);
|
this.location.go(path);
|
||||||
|
|
|
@ -872,6 +872,44 @@ describe('Integration', () => {
|
||||||
|
|
||||||
expect(location.path()).toEqual('/team/22');
|
expect(location.path()).toEqual('/team/22');
|
||||||
})));
|
})));
|
||||||
|
|
||||||
|
it('should not break the back button when trigger by location change',
|
||||||
|
fakeAsync(inject(
|
||||||
|
[Router, TestComponentBuilder, Location],
|
||||||
|
(router: Router, tcb: TestComponentBuilder, location: Location) => {
|
||||||
|
const fixture = tcb.createFakeAsync(RootCmp);
|
||||||
|
advance(fixture);
|
||||||
|
router.resetConfig([
|
||||||
|
{path: 'initial', component: BlankCmp},
|
||||||
|
{path: 'old/team/:id', redirectTo: 'team/:id'},
|
||||||
|
{path: 'team/:id', component: TeamCmp}
|
||||||
|
]);
|
||||||
|
|
||||||
|
location.go('initial');
|
||||||
|
location.go('old/team/22');
|
||||||
|
|
||||||
|
// initial navigation
|
||||||
|
router.initialNavigation();
|
||||||
|
advance(fixture);
|
||||||
|
expect(location.path()).toEqual('/team/22');
|
||||||
|
|
||||||
|
location.back();
|
||||||
|
advance(fixture);
|
||||||
|
expect(location.path()).toEqual('/initial');
|
||||||
|
|
||||||
|
// location change
|
||||||
|
(<any>location).go('/old/team/33');
|
||||||
|
|
||||||
|
|
||||||
|
advance(fixture);
|
||||||
|
expect(location.path()).toEqual('/team/33');
|
||||||
|
|
||||||
|
location.back();
|
||||||
|
advance(fixture);
|
||||||
|
expect(location.path()).toEqual('/initial');
|
||||||
|
})));
|
||||||
|
|
||||||
|
// should not break the back button when trigger by initial navigation
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('guards', () => {
|
describe('guards', () => {
|
||||||
|
|
|
@ -108,6 +108,7 @@ export interface NavigationExtras {
|
||||||
preserveQueryParams?: boolean;
|
preserveQueryParams?: boolean;
|
||||||
queryParams?: Params;
|
queryParams?: Params;
|
||||||
relativeTo?: ActivatedRoute;
|
relativeTo?: ActivatedRoute;
|
||||||
|
replaceUrl?: boolean;
|
||||||
skipLocationChange?: boolean;
|
skipLocationChange?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue