Skip to content

Commit b035fdd

Browse files
committed
fix(router): don't intercept downloads with the Navigation API
With the platform navigation integration, the router intercepts every same-origin `navigate` event it can, except reloads. That includes `<a download>` links, which then become same-document navigations: the file is never downloaded. Leave navigate events that have a `downloadRequest` to the browser, the same way reloads already are. Fixes angular#71551
1 parent a0702f0 commit b035fdd

2 files changed

Lines changed: 28 additions & 1 deletion

File tree

‎packages/router/src/statemanager/navigation_state_manager.ts‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,9 @@ export class NavigationStateManager extends StateManager {
405405
// We also do not convert reload navigation events to SPA navigations. Intercepting
406406
// would prevent the generally expected hard refresh. If an application wants special
407407
// handling for reloads, they can implement it in their own `navigate` event listener.
408-
if (!event.canIntercept || event.navigationType === 'reload') {
408+
// Downloads are left to the browser as well. Intercepting them would turn them into
409+
// same-document navigations and the file would never be downloaded.
410+
if (!event.canIntercept || event.navigationType === 'reload' || event.downloadRequest != null) {
409411
return;
410412
}
411413

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,5 +484,30 @@ if (typeof window !== 'undefined' && 'navigation' in window && !isFirefox()) {
484484
await timeout(3);
485485
expect(routerEvents).toEqual([]);
486486
});
487+
488+
/**
489+
* Cancels the next `navigate` event so the browser doesn't download the file.
490+
* The listener is added after the router's own listener, so the router has already handled
491+
* the event when it runs.
492+
*/
493+
function preventNextNavigation() {
494+
TestBed.inject(PlatformNavigation).addEventListener(
495+
'navigate',
496+
(e: NavigateEvent) => e.preventDefault(),
497+
{once: true},
498+
);
499+
}
500+
501+
it(
502+
'should not convert downloads to SPA navigations',
503+
withBody('<a href="/file.txt" download>download</a>', async () => {
504+
preventNextNavigation();
505+
const routerEvents: Event[] = [];
506+
router.events.subscribe((e) => routerEvents.push(e));
507+
document.querySelector('a')!.click();
508+
await timeout();
509+
expect(routerEvents).toEqual([]);
510+
}),
511+
);
487512
});
488513
}

0 commit comments

Comments
 (0)