Skip to content

Commit 1976e37

Browse files
nebkatdylhunn
authored andcommitted
fix(router): restore 'history.state' on popstate even if navigationId missing (#48033)
If `history.pushState()` or `history.replaceState()` were called manually without including the `navigationId` field the state was being incorrectly discarded - that logic was for maintaining the original behavior of `NavigationStart.restoredState`. Improves on #28176, fully fixes #28108, see also #28954 PR Close #48033
1 parent 4ce9650 commit 1976e37

File tree

3 files changed

+32
-8
lines changed

3 files changed

+32
-8
lines changed

packages/common/testing/src/location_mock.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,15 @@ export class SpyLocation implements Location {
103103
path = this.prepareExternalUrl(path);
104104

105105
const history = this._history[this._historyIndex];
106+
107+
history.state = state;
108+
106109
if (history.path == path && history.query == query) {
107110
return;
108111
}
109112

110113
history.path = path;
111114
history.query = query;
112-
history.state = state;
113115

114116
const url = path + (query.length > 0 ? ('?' + query) : '');
115117
this.urlChanges.push('replace: ' + url);

packages/router/src/router.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,19 +1051,30 @@ export class Router {
10511051
// hybrid apps.
10521052
setTimeout(() => {
10531053
const extras: NavigationExtras = {replaceUrl: true};
1054-
// Navigations coming from Angular router have a navigationId state
1055-
// property. When this exists, restore the state.
1056-
const state = event.state?.navigationId ? event.state : null;
1057-
if (state) {
1058-
const stateCopy = {...state} as Partial<RestoredState>;
1054+
1055+
// TODO: restoredState should always include the entire state, regardless
1056+
// of navigationId. This requires a breaking change to update the type on
1057+
// NavigationStart’s restoredState, which currently requires navigationId
1058+
// to always be present. The Router used to only restore history state if
1059+
// a navigationId was present.
1060+
1061+
// The stored navigationId is used by the RouterScroller to retrieve the scroll
1062+
// position for the page.
1063+
const restoredState = event.state?.navigationId ? event.state : null;
1064+
1065+
// Separate to NavigationStart.restoredState, we must also restore the state to
1066+
// history.state and generate a new navigationId, since it will be overwritten
1067+
if (event.state) {
1068+
const stateCopy = {...event.state} as Partial<RestoredState>;
10591069
delete stateCopy.navigationId;
10601070
delete stateCopy.ɵrouterPageId;
10611071
if (Object.keys(stateCopy).length !== 0) {
10621072
extras.state = stateCopy;
10631073
}
10641074
}
1075+
10651076
const urlTree = this.parseUrl(event['url']!);
1066-
this.scheduleNavigation(urlTree, source, state, extras);
1077+
this.scheduleNavigation(urlTree, source, restoredState, extras);
10671078
}, 0);
10681079
}
10691080
});

packages/router/test/integration.spec.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ describe('Integration', () => {
163163
}
164164
});
165165

166-
const state = {foo: 'bar'};
166+
let state: any = {foo: 'bar'};
167167
router.navigateByUrl('/simple', {state});
168168
tick();
169169
location.back();
@@ -173,6 +173,17 @@ describe('Integration', () => {
173173

174174
expect(navigation.extras.state).toBeDefined();
175175
expect(navigation.extras.state).toEqual(state);
176+
177+
// Manually set state rather than using navigate()
178+
state = {bar: 'foo'};
179+
location.replaceState(location.path(), '', state);
180+
location.back();
181+
tick();
182+
location.forward();
183+
tick();
184+
185+
expect(navigation.extras.state).toBeDefined();
186+
expect(navigation.extras.state).toEqual(state);
176187
})));
177188

178189
it('should navigate correctly when using `Location#historyGo',

0 commit comments

Comments
 (0)