Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(router): restore 'history.state' on popstate even if navigationId missing #48033

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/common/testing/src/location_mock.ts
Expand Up @@ -103,13 +103,15 @@ export class SpyLocation implements Location {
path = this.prepareExternalUrl(path);

const history = this._history[this._historyIndex];

history.state = state;

if (history.path == path && history.query == query) {
return;
}

history.path = path;
history.query = query;
history.state = state;

const url = path + (query.length > 0 ? ('?' + query) : '');
this.urlChanges.push('replace: ' + url);
Expand Down
23 changes: 17 additions & 6 deletions packages/router/src/router.ts
Expand Up @@ -1018,19 +1018,30 @@ export class Router {
// hybrid apps.
setTimeout(() => {
const extras: NavigationExtras = {replaceUrl: true};
// Navigations coming from Angular router have a navigationId state
// property. When this exists, restore the state.
const state = event.state?.navigationId ? event.state : null;
if (state) {
const stateCopy = {...state} as Partial<RestoredState>;

// TODO: restoredState should always include the entire state, regardless
// of navigationId. This requires a breaking change to update the type on
// NavigationStart’s restoredState, which currently requires navigationId
// to always be present. The Router used to only restore history state if
// a navigationId was present.

// The stored navigationId is used by the RouterScroller to retrieve the scroll
// position for the page.
const restoredState = event.state?.navigationId ? event.state : null;

// Separate to NavigationStart.restoredState, we must also restore the state to
// history.state and generate a new navigationId, since it will be overwritten
if (event.state) {
const stateCopy = {...event.state} as Partial<RestoredState>;
delete stateCopy.navigationId;
delete stateCopy.ɵrouterPageId;
if (Object.keys(stateCopy).length !== 0) {
extras.state = stateCopy;
}
}

const urlTree = this.parseUrl(event['url']!);
this.scheduleNavigation(urlTree, source, state, extras);
this.scheduleNavigation(urlTree, source, restoredState, extras);
}, 0);
}
});
Expand Down
13 changes: 12 additions & 1 deletion packages/router/test/integration.spec.ts
Expand Up @@ -163,7 +163,7 @@ describe('Integration', () => {
}
});

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

expect(navigation.extras.state).toBeDefined();
expect(navigation.extras.state).toEqual(state);

// Manually set state rather than using navigate()
state = {bar: 'foo'};
location.replaceState(location.path(), '', state);
location.back();
tick();
location.forward();
tick();

expect(navigation.extras.state).toBeDefined();
expect(navigation.extras.state).toEqual(state);
})));

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