fix(router): location changes and redirects break the back button (#10742)

This commit is contained in:
Victor Savkin 2016-08-12 14:30:51 -07:00 committed by vikerman
parent 203b2ba637
commit 04c6b2fe85
4 changed files with 50 additions and 6 deletions

View File

@ -77,6 +77,7 @@ export class SpyLocation implements Location {
var url = path + (query.length > 0 ? ('?' + query) : '');
this.urlChanges.push(url);
this._subject.emit({'url': url, 'pop': false});
}
replaceState(path: string, query: string = '') {

View File

@ -47,6 +47,7 @@ export interface NavigationExtras {
preserveQueryParams?: boolean;
preserveFragment?: boolean;
skipLocationChange?: boolean;
replaceUrl?: boolean;
}
/**
@ -160,7 +161,7 @@ export class Router {
*/
initialNavigation(): void {
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> {
const id = ++this.navigationId;
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 {
@ -347,12 +349,14 @@ export class Router {
// we fire multiple events for a single URL change
// we should navigate only once
return this.currentUrlTree.toString() !== tree.toString() ?
this.scheduleNavigation(tree, change['pop']) :
this.scheduleNavigation(tree, {skipLocationChange: change['pop'], replaceUrl: true}) :
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) {
this.location.go(this.urlSerializer.serialize(this.currentUrlTree));
this.routerEvents.next(new NavigationCancel(id, this.serializeUrl(url)));
@ -415,9 +419,9 @@ export class Router {
new ActivateRoutes(state, storedState).activate(this.outletMap);
if (!preventPushState) {
if (!shouldPreventPushState) {
let path = this.urlSerializer.serialize(appliedUrl);
if (this.location.isCurrentPathEqualTo(path)) {
if (this.location.isCurrentPathEqualTo(path) || shouldReplaceUrl) {
this.location.replaceState(path);
} else {
this.location.go(path);

View File

@ -872,6 +872,44 @@ describe('Integration', () => {
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', () => {

View File

@ -108,6 +108,7 @@ export interface NavigationExtras {
preserveQueryParams?: boolean;
queryParams?: Params;
relativeTo?: ActivatedRoute;
replaceUrl?: boolean;
skipLocationChange?: boolean;
}