feat(router): add navigationSource and restoredState to NavigationStart event (#21728)

Currently, NavigationStart there is no way to know if an navigation was triggered imperatively or via the location change. These two use cases should be handled differently for a variety of use cases (e.g., scroll position restoration). This PR adds a navigation source field and restored navigation id (passed to navigations triggered by a URL change).

PR Close #21728
This commit is contained in:
vsavkin
2018-01-24 12:19:59 -05:00
committed by Jason Aden
parent 5bd93b1f0f
commit c40ae7f7cf
10 changed files with 183 additions and 40 deletions

View File

@ -76,6 +76,28 @@ describe('Integration', () => {
]);
})));
it('should set the restoredState to null when executing imperative navigations',
fakeAsync(inject([Router], (router: Router) => {
router.resetConfig([
{path: '', component: SimpleCmp},
{path: 'simple', component: SimpleCmp},
]);
const fixture = createRoot(router, RootCmp);
let event: NavigationStart;
router.events.subscribe(e => {
if (e instanceof NavigationStart) {
event = e;
}
});
router.navigateByUrl('/simple');
tick();
expect(event !.navigationTrigger).toEqual('imperative');
expect(event !.restoredState).toEqual(null);
})));
it('should not pollute browser history when replaceUrl is set to true',
fakeAsync(inject([Router, Location], (router: Router, location: SpyLocation) => {
router.resetConfig([
@ -466,21 +488,34 @@ describe('Integration', () => {
[{path: 'simple', component: SimpleCmp}, {path: 'user/:name', component: UserCmp}]
}]);
let event: NavigationStart;
router.events.subscribe(e => {
if (e instanceof NavigationStart) {
event = e;
}
});
router.navigateByUrl('/team/33/simple');
advance(fixture);
expect(location.path()).toEqual('/team/33/simple');
const simpleNavStart = event !;
router.navigateByUrl('/team/22/user/victor');
advance(fixture);
const userVictorNavStart = event !;
location.back();
advance(fixture);
expect(location.path()).toEqual('/team/33/simple');
expect(event !.navigationTrigger).toEqual('hashchange');
expect(event !.restoredState !.navigationId).toEqual(simpleNavStart.id);
location.forward();
advance(fixture);
expect(location.path()).toEqual('/team/22/user/victor');
expect(event !.navigationTrigger).toEqual('hashchange');
expect(event !.restoredState !.navigationId).toEqual(userVictorNavStart.id);
})));
it('should navigate to the same url when config changes',
@ -868,6 +903,40 @@ describe('Integration', () => {
expect(locationUrlBeforeEmittingError).toEqual('/simple');
}));
it('should reset the url with the right state when navigation errors', fakeAsync(() => {
const router: Router = TestBed.get(Router);
const location: SpyLocation = TestBed.get(Location);
const fixture = createRoot(router, RootCmp);
router.resetConfig([
{path: 'simple1', component: SimpleCmp}, {path: 'simple2', component: SimpleCmp},
{path: 'throwing', component: ThrowingCmp}
]);
let event: NavigationStart;
router.events.subscribe(e => {
if (e instanceof NavigationStart) {
event = e;
}
});
router.navigateByUrl('/simple1');
advance(fixture);
const simple1NavStart = event !;
router.navigateByUrl('/throwing').catch(() => null);
advance(fixture);
router.navigateByUrl('/simple2');
advance(fixture);
location.back();
tick();
expect(event !.restoredState !.navigationId).toEqual(simple1NavStart.id);
}));
it('should not trigger another navigation when resetting the url back due to a NavigationError',
fakeAsync(() => {
const router = TestBed.get(Router);