From df76a2048b92c2cb977fcc6d884a898c712e5068 Mon Sep 17 00:00:00 2001 From: Alexander Vologin Date: Wed, 16 Jan 2019 04:09:03 -0500 Subject: [PATCH] fix(router): restore 'history.state' object for navigations coming from Angular router (#28108) (#28176) When navigations coming from Angular router we may have a payload stored in state property. When this exists, set extras's state to the payload. PR Close #28176 --- packages/router/src/router.ts | 10 ++++- packages/router/test/integration.spec.ts | 53 ++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/packages/router/src/router.ts b/packages/router/src/router.ts index 83b915fa3c..a4e1dae9fa 100644 --- a/packages/router/src/router.ts +++ b/packages/router/src/router.ts @@ -920,7 +920,15 @@ export class Router { // hybrid apps. setTimeout(() => { const {source, state, urlTree} = currentChange; - this.scheduleNavigation(urlTree, source, state, {replaceUrl: true}); + const extras: NavigationExtras = {replaceUrl: true}; + if (state) { + const stateCopy = {...state}; + delete stateCopy.navigationId; + if (Object.keys(stateCopy).length !== 0) { + extras.state = stateCopy; + } + } + this.scheduleNavigation(urlTree, source, state, extras); }, 0); } this.lastLocationChangeInfo = currentChange; diff --git a/packages/router/test/integration.spec.ts b/packages/router/test/integration.spec.ts index ce343becea..47d4edbbce 100644 --- a/packages/router/test/integration.spec.ts +++ b/packages/router/test/integration.spec.ts @@ -159,6 +159,59 @@ describe('Integration', () => { expect(navigation.extras.state).toEqual({foo: 'bar'}); }))); + it('should set history.state when navigation with browser back and forward', + fakeAsync(inject([Router, Location], (router: Router, location: SpyLocation) => { + router.resetConfig([ + {path: '', component: SimpleCmp}, + {path: 'simple', component: SimpleCmp}, + ]); + + const fixture = createRoot(router, RootCmp); + let navigation: Navigation = null!; + router.events.subscribe(e => { + if (e instanceof NavigationStart) { + navigation = router.getCurrentNavigation()!; + } + }); + + const state = {foo: 'bar'}; + router.navigateByUrl('/simple', {state}); + tick(); + location.back(); + tick(); + location.forward(); + tick(); + + expect(navigation.extras.state).toBeDefined(); + expect(navigation.extras.state).toEqual(state); + }))); + + it('should not error if state is not {[key: string]: any}', + fakeAsync(inject([Router, Location], (router: Router, location: SpyLocation) => { + router.resetConfig([ + {path: '', component: SimpleCmp}, + {path: 'simple', component: SimpleCmp}, + ]); + + const fixture = createRoot(router, RootCmp); + let navigation: Navigation = null!; + router.events.subscribe(e => { + if (e instanceof NavigationStart) { + navigation = router.getCurrentNavigation()!; + } + }); + + location.replaceState('', '', 42); + router.navigateByUrl('/simple'); + tick(); + location.back(); + advance(fixture); + + // Angular does not support restoring state to the primitive. + expect(navigation.extras.state).toEqual(undefined); + expect(location.getState()).toEqual({navigationId: 3}); + }))); + it('should not pollute browser history when replaceUrl is set to true', fakeAsync(inject([Router, Location], (router: Router, location: SpyLocation) => { router.resetConfig([