Skip to content

Commit fa5528f

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 f612fce commit fa5528f

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
@@ -1017,19 +1017,30 @@ export class Router {
10171017
// hybrid apps.
10181018
setTimeout(() => {
10191019
const extras: NavigationExtras = {replaceUrl: true};
1020-
// Navigations coming from Angular router have a navigationId state
1021-
// property. When this exists, restore the state.
1022-
const state = event.state?.navigationId ? event.state : null;
1023-
if (state) {
1024-
const stateCopy = {...state} as Partial<RestoredState>;
1020+
1021+
// TODO: restoredState should always include the entire state, regardless
1022+
// of navigationId. This requires a breaking change to update the type on
1023+
// NavigationStart’s restoredState, which currently requires navigationId
1024+
// to always be present. The Router used to only restore history state if
1025+
// a navigationId was present.
1026+
1027+
// The stored navigationId is used by the RouterScroller to retrieve the scroll
1028+
// position for the page.
1029+
const restoredState = event.state?.navigationId ? event.state : null;
1030+
1031+
// Separate to NavigationStart.restoredState, we must also restore the state to
1032+
// history.state and generate a new navigationId, since it will be overwritten
1033+
if (event.state) {
1034+
const stateCopy = {...event.state} as Partial<RestoredState>;
10251035
delete stateCopy.navigationId;
10261036
delete stateCopy.ɵrouterPageId;
10271037
if (Object.keys(stateCopy).length !== 0) {
10281038
extras.state = stateCopy;
10291039
}
10301040
}
1041+
10311042
const urlTree = this.parseUrl(event['url']!);
1032-
this.scheduleNavigation(urlTree, source, state, extras);
1043+
this.scheduleNavigation(urlTree, source, restoredState, extras);
10331044
}, 0);
10341045
}
10351046
});

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)