Angular-Material Sidenav cdkScrollable
Asked Answered
C

2

15

The Angular Material CDK provides a Directive CdkScrollable, which allows you to listen to ScrollEvents of a specific container.
I am now trying to access the CdkScrollable of the MatSidenavContent, which is added by default.
However my @ViewChild(CdkScrollable) and @ContentChild(CdkScrollable) are always undefined.

My Component looks something like this:

<mat-sidenav-container>
    <mat-sidenav>Sidenav content</mat-sidenav>
    <div>Main content</div>
</mat-sidenav-container>

The resulting DOM looks something like this:

<mat-sidenav-container>
    <div class="mat-drawer-backdrop"></div>
    <div tabindex="-1" class="cdk-visually-hidden cdk-focus-trap-anchor"></div>
    <mat-sidenav>Sidenav content</mat-sidenav>
    <mat-sidenav-content cdkScrollable>
          <div>Main content</div>
    </mat-sidenav-content>
</mat-sidenav-container>

The mat-sidenav-content Component, which is generated automatically, uses a CdkScrollable-Directive, which I need to access.
My question is now:
Is it possible to access that Directive and if so, how?

Clop answered 28/11, 2017 at 10:9 Comment(3)
It's funny that the material docs show this as an example but no matter what i try, ALL NIGHT, it does not work.Ortego
@Ortego I opened an Issue on @angular/material some time ago and the CdkScrollable instance is now accessible. See hereClop
Thanks for the help Springbua. I was never able to get the this.sidenavContainer.scrollable to be anything other than undefined. I had to instead use only CdkScrollable. @ViewChild(MatSidenavContent) still to this day does not work for me trying to get the scroll. It is very weird. Thanks again! I am glad you got it to work!Ortego
C
6

I opened an Issue on @angular/material some time ago and they now expose the CdkScrollable-Instance.
To use it, you need to access the MatSidenavContainer using @ViewChild(MatSidenavContainer. This instance has a public member scrollable, which is the CdkScrollable instance.
An example can be found here

Edit: As the example is not very complete and a few people are having difficulties implementing it, I'll write my own example here:

HTML:

<mat-sidenav-container>
    <mat-sidenav #sidenav>
        Sidenav Content
    </mat-sidenav>
    <div>
        Main Content
    </div>
</mat-sidenav-container>

TypeScript:

import { Component, AfterViewInit, ViewChild } from '@angular/core';
import { MatSidenavContainer } from '@angular/material';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent implements AfterViewInit  {
  @ViewChild(MatSidenavContainer) sidenavContainer: MatSidenavContainer;

    constructor() {
    }

    ngAfterViewInit() {
      console.log(this.sidenavContainer.scrollable);
    }
}

Important:

  1. Don't use <mat-sidenav-content>. This tag is generated automatically and it has the cdkScrollable directive attached to it. If you use <mat-sidenav-content> in your own template, the scrollable will be undefined.
  2. Use AfterViewInit instead of OnInit. As much as I know, @ViewChild is resolved in AfterViewInit, OnInit is probably too early.
Clop answered 27/4, 2018 at 13:53 Comment(5)
I can't get it to work. Here my example. I also found this issue on @angular/material.Amy
@Amy I edited my answer. The problem in your example is, that you wirte <mat-sidenav-content> yourself. This tag is automatically added by angular material and includes the cdkScrollable. In your example this is missing in the final HTML (I have seen that in the developer tools). Also you are using ngOnInit, but you should use AfterViewInit, if you are working with ViewChild.Clop
Thank you so much @Springrbua! I made a new example on Stackblitz. Now I can subscribe to this.sidenavContainer.scrollable.elementScrolled() but nothing happens.Amy
Take a look at material.angular.io/cdk/scrolling/api#CdkScrollable The API of CdkScrollable should help. If not, feel free to create a new question and let me know about it.Clop
#1 under important just saved my mental state. thank youYellowlegs
P
22
  1. Add to your app module imports: ScrollDispatchModule.
  2. Add cdkScrollable to your mat-sidenav-content:

<mat-sidenav-content cdkScrollable> </mat-sidenav-content>

  1. In your root component:

a) inject ScrollDispatcher from @angular/cdk/overlay and subscribe to scrolling:

constructor(public scroll: ScrollDispatcher) {

    this.scrollingSubscription = this.scroll
          .scrolled()
          .subscribe((data: CdkScrollable) => {
            this.onWindowScroll(data);
          });
}

c) do something when scrolling, e.g. check the offset

private onWindowScroll(data: CdkScrollable) {
    const scrollTop = data.getElementRef().nativeElement.scrollTop || 0;
    if (this.lastOffset > scrollTop) {
      // console.log('Show toolbar');
    } else if (scrollTop < 10) {
      // console.log('Show toolbar');
    } else if (scrollTop > 100) {
      // console.log('Hide toolbar');
    }

    this.lastOffset = scrollTop;
  }

Documentation: https://material.angular.io/cdk/scrolling/api

Update Angular 9 :

Use import {ScrollingModule} from '@angular/cdk/scrolling', ScrollDispatchModule is deprecated

Perspicuous answered 12/6, 2018 at 8:38 Comment(6)
I think you can also do it like this? const scrollable = this.scrollDispatcher.getAncestorScrollContainers(this.elementRef)[0]; console.log(scrollable.getElementRef().nativeElement.scrollTop);Berwick
Could you explain how to use 'elementScrolled' method of 'CdkScrollable' directive in place of scroll.scrolled?Castled
data is always undefined. Why?Firebug
FYI - ScrollDispatchModule has been renamed to ScrollingModulePhotolithography
@Firebug this could help programmersought.com/article/3361447407/…Id
Yes the data is undefined for me too. @Firebug did u find any resolution to it ?Zulemazullo
C
6

I opened an Issue on @angular/material some time ago and they now expose the CdkScrollable-Instance.
To use it, you need to access the MatSidenavContainer using @ViewChild(MatSidenavContainer. This instance has a public member scrollable, which is the CdkScrollable instance.
An example can be found here

Edit: As the example is not very complete and a few people are having difficulties implementing it, I'll write my own example here:

HTML:

<mat-sidenav-container>
    <mat-sidenav #sidenav>
        Sidenav Content
    </mat-sidenav>
    <div>
        Main Content
    </div>
</mat-sidenav-container>

TypeScript:

import { Component, AfterViewInit, ViewChild } from '@angular/core';
import { MatSidenavContainer } from '@angular/material';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent implements AfterViewInit  {
  @ViewChild(MatSidenavContainer) sidenavContainer: MatSidenavContainer;

    constructor() {
    }

    ngAfterViewInit() {
      console.log(this.sidenavContainer.scrollable);
    }
}

Important:

  1. Don't use <mat-sidenav-content>. This tag is generated automatically and it has the cdkScrollable directive attached to it. If you use <mat-sidenav-content> in your own template, the scrollable will be undefined.
  2. Use AfterViewInit instead of OnInit. As much as I know, @ViewChild is resolved in AfterViewInit, OnInit is probably too early.
Clop answered 27/4, 2018 at 13:53 Comment(5)
I can't get it to work. Here my example. I also found this issue on @angular/material.Amy
@Amy I edited my answer. The problem in your example is, that you wirte <mat-sidenav-content> yourself. This tag is automatically added by angular material and includes the cdkScrollable. In your example this is missing in the final HTML (I have seen that in the developer tools). Also you are using ngOnInit, but you should use AfterViewInit, if you are working with ViewChild.Clop
Thank you so much @Springrbua! I made a new example on Stackblitz. Now I can subscribe to this.sidenavContainer.scrollable.elementScrolled() but nothing happens.Amy
Take a look at material.angular.io/cdk/scrolling/api#CdkScrollable The API of CdkScrollable should help. If not, feel free to create a new question and let me know about it.Clop
#1 under important just saved my mental state. thank youYellowlegs

© 2022 - 2024 — McMap. All rights reserved.