Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular-Material Sidenav cdkScrollable

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?

like image 668
Robert P Avatar asked Nov 28 '17 10:11

Robert P


People also ask

How do you fix Sidenav mats?

For <mat-sidenav> only (not <mat-drawer> ) fixed positioning is supported. It can be enabled by setting the fixedInViewport property. Additionally, top and bottom space can be set via the fixedTopGap and fixedBottomGap. These properties accept a pixel value amount of space to add at the top or bottom.

What is cdkScrollable?

cdkScrollable and ScrollDispatcher The cdkScrollable directive and the ScrollDispatcher service together allow components to react to scrolling in any of its ancestor scrolling containers. The cdkScrollable directive should be applied to any element that acts as a scrolling container.

How do I turn off Sidenav on click?

A <mat-sidenav> can be opened or closed using the open() , close() and toggle() methods.


2 Answers

  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

like image 80
Sebastian Denis Avatar answered Oct 21 '22 20:10

Sebastian Denis


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.
like image 21
Robert P Avatar answered Oct 21 '22 19:10

Robert P