-
-
Save iffa/9c820072135d25a6372d58075fe264dd to your computer and use it in GitHub Desktop.
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]; | |
} | |
} |
Thanks!
thanks! but I have one question. is it ok to not unsubscribe from this subscription in case of just frontend app / in case of the app with server-side rendering?
It is also working for me, thanks!
I have one more improvement. Add the following line before calling this.handleScrollOnNavigation();
this.viewportScroller.setHistoryScrollRestoration('manual');
This way automatic scrolling from browser will be turned off so there are no scrolling results race conditions. In my case on navigating back I first saw scroll to top even before route change and before this.viewportScroller.scrollToPosition(current.position);
was called,
@anbaran Thanks, updated code to reflect this.
👍
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.
Thanks for sharing!