Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 6449590

Browse filesBrowse files
atscottdylhunn
authored andcommitted
fix(router): eagerly update internal state on browser-triggered navigations (#43102)
The management of `browserUrlTree` currently has several problems with correctly tracking the actual state of the browser. This change makes the Router eagerly update the `browserUrlTree` when handling navigations triggered by browser events (i.e., not 'imperative'). This is because with those types of navigations, the browser URL bar is _already_ updated. If we do not update the internal tracking of the `browserUrlTree`, we will be out of sync with the real URL if the navigation is rejected. It would be best if we could remove `browserUrlTree` completely, but doing that would require a lot more investigation and is blocked by #27059 because the SpyLocation used in tests does not emulate real browser behavior. fixes #43101 PR Close #43102
1 parent 1939613 commit 6449590
Copy full SHA for 6449590

File tree

Expand file treeCollapse file tree

4 files changed

+29
-12
lines changed
Filter options
Expand file treeCollapse file tree

4 files changed

+29
-12
lines changed

‎packages/core/test/bundling/router/bundle.golden_symbols.json

Copy file name to clipboardExpand all lines: packages/core/test/bundling/router/bundle.golden_symbols.json
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1526,6 +1526,9 @@
15261526
{
15271527
"name": "isArrayLike"
15281528
},
1529+
{
1530+
"name": "isBrowserTriggeredNavigation"
1531+
},
15291532
{
15301533
"name": "isCommandWithOutlets"
15311534
},

‎packages/router/src/router.ts

Copy file name to clipboardExpand all lines: packages/router/src/router.ts
+18-3Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,12 @@ export class Router {
636636
(this.onSameUrlNavigation === 'reload' ? true : urlTransition) &&
637637
this.urlHandlingStrategy.shouldProcessUrl(t.rawUrl);
638638

639+
// If the source of the navigation is from a browser event, the URL is
640+
// already updated. We already need to sync the internal state.
641+
if (isBrowserTriggeredNavigation(t.source)) {
642+
this.browserUrlTree = t.rawUrl;
643+
}
644+
639645
if (processCurrentUrl) {
640646
return of(t).pipe(
641647
// Fire NavigationStart event
@@ -954,7 +960,12 @@ export class Router {
954960
this.urlHandlingStrategy.merge(e.url, this.rawUrlTree);
955961
const extras = {
956962
skipLocationChange: t.extras.skipLocationChange,
957-
replaceUrl: this.urlUpdateStrategy === 'eager'
963+
// The URL is already updated at this point if we have 'eager' URL
964+
// updates or if the navigation was triggered by the browser (back
965+
// button, URL bar, etc). We want to replace that item in history if
966+
// the navigation is rejected.
967+
replaceUrl: this.urlUpdateStrategy === 'eager' ||
968+
isBrowserTriggeredNavigation(t.source)
958969
};
959970

960971
this.scheduleNavigation(
@@ -1385,8 +1396,8 @@ export class Router {
13851396
const lastNavigation = this.getTransition();
13861397
// We don't want to skip duplicate successful navs if they're imperative because
13871398
// onSameUrlNavigation could be 'reload' (so the duplicate is intended).
1388-
const browserNavPrecededByRouterNav =
1389-
source !== 'imperative' && lastNavigation?.source === 'imperative';
1399+
const browserNavPrecededByRouterNav = isBrowserTriggeredNavigation(source) && lastNavigation &&
1400+
!isBrowserTriggeredNavigation(lastNavigation.source);
13901401
const lastNavigationSucceeded = this.lastSuccessfulId === lastNavigation.id;
13911402
// If the last navigation succeeded or is in flight, we can use the rawUrl as the comparison.
13921403
// However, if it failed, we should compare to the final result (urlAfterRedirects).
@@ -1549,3 +1560,7 @@ function validateCommands(commands: string[]): void {
15491560
}
15501561
}
15511562
}
1563+
1564+
function isBrowserTriggeredNavigation(source: 'imperative'|'popstate'|'hashchange') {
1565+
return source !== 'imperative';
1566+
}

‎packages/router/test/integration.spec.ts

Copy file name to clipboardExpand all lines: packages/router/test/integration.spec.ts
+5-1Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1230,7 +1230,11 @@ describe('Integration', () => {
12301230
location.simulateHashChange('/blocked');
12311231

12321232
advance(fixture);
1233-
expectEvents(recordedEvents, [[NavigationStart, '/blocked']]);
1233+
expectEvents(recordedEvents, [
1234+
[NavigationStart, '/blocked'],
1235+
[NavigationStart, '/simple'],
1236+
[NavigationEnd, '/simple'],
1237+
]);
12341238
}));
12351239
});
12361240

‎packages/router/test/regression_integration.spec.ts

Copy file name to clipboardExpand all lines: packages/router/test/regression_integration.spec.ts
+3-8Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -266,14 +266,9 @@ describe('Integration', () => {
266266
location.simulateHashChange('/one');
267267
advance(fixture);
268268

269-
const BASE_ERROR_MESSAGE =
270-
'This asserts current behavior, which is incorrect. When #28561 is fixed, it should be: ';
271-
272-
expect(location.path()).toEqual('/one', BASE_ERROR_MESSAGE + '/');
273-
const urlChanges = ['replace: /', 'hash: /one'];
274-
expect(location.urlChanges)
275-
.toEqual(
276-
urlChanges, BASE_ERROR_MESSAGE + JSON.stringify(urlChanges.concat('replace: /')));
269+
expect(location.path()).toEqual('/');
270+
const urlChanges = ['replace: /', 'hash: /one', 'replace: /'];
271+
expect(location.urlChanges).toEqual(urlChanges);
277272
}));
278273
});
279274
});

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.