I am having the exact same problem.
I solved it by:
- For the mat-side-nav-container, instead of setting 'height', I set 'min-height' as 100%, so that the scrollable element is the body, and not the sidenav container, but in case the content is less than the viewport, the container will still stretch to cover it. Please remember, this may affect your layout depending on your implementation, but this is absolutely essential for this solution to work.
- The height of the mat-toolbar (above the mat-sidenav-container) is 64px and the margin I keep for the element being scrolled is 16px, therefore (64 + 16) is the y-offset
- In app.module.ts, inside imports for the NgModule
RouterModule.forRoot(
routes,
{
anchorScrolling: 'enabled',
scrollOffset: [0, 64 + 16]
}),
- Assuming the mat-sidenav-container lives in app.component.html, I added the following to app.component.ts inside ngOnInit().
(Please remember to take precautions against memory leaks due to subscriptions, although this is the app-component, so it doesn't really matter)
this.router.events
.pipe(
//take only scroll events that have an anchor specified
filter(e => e instanceof Scroll && !!e.anchor),
//wait for the DOM to resolve. It worked with 10, but it was a small test case
//so I used 100 just in case
delay(100),
//take the element that the anchor points to
map((e: Scroll) => document.getElementById(e.anchor)),
//ignore if no element was found
filter(el => !!el)
)
.subscribe(el =>
document.scrollingElement
.scroll(
0,
window.scrollY + el.getBoundingClientRect().top - (64 + 16)
)));
Additional improvements:
- Instead of hardcoding the y-offset - one can query for the clientHeight of the mat-toobar and the clientTop of the element being scrolled to, and add them to get the offset
- I have only tested this for scrolling to elements that are direct descendants of mat-sidenav-content. Maybe this logic can be extended to work for nested elements, nested routes etc etc etc