Skip to content

Commit

Permalink
fix(router): don't break history when CanDeactivate cancel back
Browse files Browse the repository at this point in the history
navigation

Closes #13586
  • Loading branch information
Dzmitry Shylovich committed Jan 14, 2017
1 parent 5237b1c commit d1667ff
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 6 deletions.
17 changes: 12 additions & 5 deletions modules/@angular/router/src/router.ts
Expand Up @@ -763,8 +763,9 @@ export class Router {
id, this.serializeUrl(url), this.serializeUrl(this.currentUrlTree)));
resolvePromise(true);
} else {
this.resetUrlToCurrentUrlTree();
this.routerEvents.next(new NavigationCancel(id, this.serializeUrl(url), ''));
const urlStr: string = this.serializeUrl(url);
this.resetUrlToCurrentUrlTree(urlStr);
this.routerEvents.next(new NavigationCancel(id, urlStr, ''));
resolvePromise(false);
}
},
Expand Down Expand Up @@ -792,9 +793,15 @@ export class Router {
});
}

private resetUrlToCurrentUrlTree(): void {
const path = this.urlSerializer.serialize(this.rawUrlTree);
this.location.replaceState(path);
private resetUrlToCurrentUrlTree(navigatedUrl?: string): void {
const prevUrl: string = this.urlSerializer.serialize(this.navigations.value.rawUrl);
const currentUrl: string = this.urlSerializer.serialize(this.rawUrlTree);
if (navigatedUrl != null && navigatedUrl === prevUrl) {
// attempt to navigate back
this.location.go(currentUrl);
} else {
this.location.replaceState(currentUrl);
}
}
}

Expand Down
83 changes: 82 additions & 1 deletion modules/@angular/router/test/integration.spec.ts
Expand Up @@ -1754,7 +1754,6 @@ describe('Integration', () => {
})));
});


describe('should work when returns an observable', () => {
beforeEach(() => {
TestBed.configureTestingModule({
Expand Down Expand Up @@ -1783,6 +1782,88 @@ describe('Integration', () => {
})));
});
});

describe('should not break history if navigation is canceled', () => {

class CancelFirstNavigation implements CanDeactivate<any> {
private firstTime: boolean = true;
canDeactivate(): boolean {
if (this.firstTime) {
this.firstTime = false;
return false;
}
return true;
}
}

@Component({selector: 'parent', template: '<router-outlet></router-outlet>'})
class Parent {
}

@Component({selector: 'home', template: 'home'})
class Home {
}

@Component({selector: 'child1', template: 'child1'})
class Child1 {
}

@Component({selector: 'child2', template: 'child2'})
class Child2 {
}

@Component({selector: 'child3', template: 'child3'})
class Child3 {
}

@NgModule({
declarations: [Parent, Home, Child1, Child2, Child3],
entryComponents: [Child1, Child2, Child3],
imports: [RouterModule]
})
class TestModule {
}

beforeEach(() => {
TestBed.configureTestingModule(
{providers: [CancelFirstNavigation], imports: [TestModule]});
});

it('works', fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
const fixture = createRoot(router, Parent);

router.resetConfig([
{path: '', component: Home}, {path: 'first', component: Child1},
{path: 'second', component: Child2},
{path: 'third', component: Child3, canDeactivate: [CancelFirstNavigation]}
]);

router.navigateByUrl('/first');
advance(fixture);
expect(location.path()).toEqual('/first');
expect(fixture.nativeElement).toHaveText('child1');

router.navigateByUrl('/second');
advance(fixture);
expect(location.path()).toEqual('/second');
expect(fixture.nativeElement).toHaveText('child2');

router.navigateByUrl('/third');
advance(fixture);
expect(location.path()).toEqual('/third');
expect(fixture.nativeElement).toHaveText('child3');

location.back();
advance(fixture);
expect(location.path()).toEqual('/third');
expect(fixture.nativeElement).toHaveText('child3');

location.back();
advance(fixture);
expect(location.path()).toEqual('/second');
expect(fixture.nativeElement).toHaveText('child2');
})));
});
});

describe('CanActivateChild', () => {
Expand Down

0 comments on commit d1667ff

Please sign in to comment.