Last active
May 16, 2024 20:48
-
-
Save iffa/9c820072135d25a6372d58075fe264dd to your computer and use it in GitHub Desktop.
Custom scroll position restoration logic for Angular 2+, that doesn't consider query parameter changes in route as forward navigation, thus preventing certain scenarios where you don't want query parameter changes to scroll-to-top as they would with 'scrollPositionRestoration: enabled'.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export class AppModule { | |
constructor(private router: Router, private viewportScroller: ViewportScroller) { | |
// Disable automatic scroll restoration to avoid race conditions | |
this.viewportScroller.setHistoryScrollRestoration('manual'); | |
this.handleScrollOnNavigation(); | |
} | |
/** | |
* When route is changed, Angular interprets a simple query params change as "forward navigation" too. | |
* Using the pairwise function allows us to have both the previous and current router events, which we can | |
* use to effectively compare the two navigation events and see if they actually change route, or only | |
* the route parameters (i.e. selections stored in query params). | |
* | |
* Related to: https://github.com/angular/angular/issues/26744 | |
*/ | |
private handleScrollOnNavigation(): void { | |
this.router.events.pipe( | |
// import { Event } from '@angular/router' | |
filter((e: Event): e is Scroll => e instanceof Scroll), | |
pairwise() | |
).subscribe((e: Scroll[]) => { | |
const previous = e[0]; | |
const current = e[1]; | |
if (current.position) { | |
// Backward navigation | |
this.viewportScroller.scrollToPosition(current.position); | |
} else if (current.anchor) { | |
// Anchor navigation | |
this.viewportScroller.scrollToAnchor(current.anchor); | |
} else { | |
// Check if routes match, or if it is only a query param change | |
if (this.getBaseRoute(previous.routerEvent.urlAfterRedirects) !== this.getBaseRoute(current.routerEvent.urlAfterRedirects)) { | |
// Routes don't match, this is actual forward navigation | |
// Default behavior: scroll to top | |
this.viewportScroller.scrollToPosition([0, 0]); | |
} | |
} | |
}); | |
} | |
private getBaseRoute(url: string): string { | |
// return url without query params | |
return url.split('?')[0]; | |
} | |
} |
👍
This may have been obvious to others but it wasn't to me. I had to change our app's router settings for scrollPositionRestoration
to:
{ scrollPositionRestoration: 'disabled' }
to get this workaround to work.
It took me a bit to figure that out. So if you are also wondering why this doesn't seem to work for you, check and make sure that is disabled.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@anbaran Thanks, updated code to reflect this.