user4229770
user4229770

Reputation:

Sticky toolbar material 2 and sidenav

Started recently learning material 2 and ran to issue. My approach is to do a sidenav as in Youtube website. There is a hamburger menu on left which expands/collapses side menu. Code:

<md-toolbar class="fixed-header">
     <button class="app-icon-button" (click)="start.toggle()">
          <i class="material-icons app-toolbar-menu">menu</i>
     </button>
</md-toolbar>

<md-sidenav-container>
    <md-sidenav #start class="app-sidebar" mode="side">test</md-sidenav>

    <div class="text">
      {{ text }}
    </div>
</md-sidenav-container>

Now seems like I achieved the layout, but the positioning is not fixed, if i scroll toolbar moves and sidenav content also moves.

Edit:

On angular material documentation I later found the example which one I have been looking for:

https://stackblitz.com/angular/rmjxrpxdgmx?file=app%2Fsidenav-responsive-example.html

if link will be removed, here is the code:

HTML File:

<div class="example-container" [class.example-is-mobile]="mobileQuery.matches" *ngIf="shouldRun">
  <mat-toolbar color="primary" class="example-toolbar">
    <button mat-icon-button (click)="snav.toggle()"><mat-icon>menu</mat-icon></button>
    <h1 class="example-app-name">Responsive App</h1>
  </mat-toolbar>

  <mat-sidenav-container class="example-sidenav-container"
                         [style.marginTop.px]="mobileQuery.matches ? 56 : 0">
    <mat-sidenav #snav [mode]="mobileQuery.matches ? 'over' : 'side'"
                 [fixedInViewport]="mobileQuery.matches" fixedTopGap="56">
      <mat-nav-list>
        <a mat-list-item routerLink="." *ngFor="let nav of fillerNav">{{nav}}</a>
      </mat-nav-list>
    </mat-sidenav>

    <mat-sidenav-content>
      <p *ngFor="let content of fillerContent">{{content}}</p>
    </mat-sidenav-content>
  </mat-sidenav-container>
</div>

TS File:

import {MediaMatcher} from '@angular/cdk/layout';
import {ChangeDetectorRef, Component, OnDestroy} from '@angular/core';

/** @title Responsive sidenav */
@Component({
  selector: 'sidenav-responsive-example',
  templateUrl: 'sidenav-responsive-example.html',
  styleUrls: ['sidenav-responsive-example.css'],
})
export class SidenavResponsiveExample implements OnDestroy {
  mobileQuery: MediaQueryList;

  fillerNav = Array.from({length: 50}, (_, i) => `Nav Item ${i + 1}`);

  fillerContent = Array.from({length: 50}, () =>
      `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
       labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
       laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in
       voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
       cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`);

  private _mobileQueryListener: () => void;

  constructor(changeDetectorRef: ChangeDetectorRef, media: MediaMatcher) {
    this.mobileQuery = media.matchMedia('(max-width: 600px)');
    this._mobileQueryListener = () => changeDetectorRef.detectChanges();
    this.mobileQuery.addListener(this._mobileQueryListener);
  }

  ngOnDestroy(): void {
    this.mobileQuery.removeListener(this._mobileQueryListener);
  }
}

CSS File:

.example-container {
  display: flex;
  flex-direction: column;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
}

.example-is-mobile .example-toolbar {
  position: fixed;
  /* Make sure the toolbar will stay on top of the content as it scrolls past. */
  z-index: 2;
}

h1.example-app-name {
  margin-left: 8px;
}

.example-sidenav-container {
  /* When the sidenav is not fixed, stretch the sidenav container to fill the available space. This
     causes `<mat-sidenav-content>` to act as our scrolling element for desktop layouts. */
  flex: 1;
}

.example-is-mobile .example-sidenav-container {
  /* When the sidenav is fixed, don't constrain the height of the sidenav container. This allows the
     `<body>` to be our scrolling element for mobile layouts. */
  flex: 1 0 auto;
}

Upvotes: 6

Views: 21738

Answers (3)

Benjamin M
Benjamin M

Reputation: 24547

I had the same problem. I fixed it with some inline CSS. Now I have:

  • fixed Toolbar Header
  • fixed, scrollable SideNav
  • scrollable Content

Here's the code:

<div style="position:absolute; top:0; bottom:0; left:0; right: 0; display: flex; flex-direction: column;">        
    <mat-toolbar color="primary">
        <button mat-icon-button (click)="sidenav.toggle()">
            <mat-icon>menu</mat-icon>
        </button>
        TOOLBAR
    </mat-toolbar>

    <mat-sidenav-container style="flex: 1;">
        <mat-sidenav mode="side" opened="true" #sidenav>
            SIDENAV
        </mat-sidenav>
        <mat-sidenav-content>
            CONTENT
        </mat-sidenav-content>
    </mat-sidenav-container>
</div>

Maybe someone can optimize my bad CSS using some angular/flex-layout directives :)

Upvotes: -1

phanf
phanf

Reputation: 821

@Nehal the example still has an issue where the <md-sidenav-container> is under the top toolbar shown here in an updated plnkr:

https://plnkr.co/edit/vXB6aUoJCkx8tAJkelbF?p=preview

If you follow the official Angular docs, see the "Fixed sidenav" example from the link below, you should get the desired look with the sticky toolbar.

https://material.angular.io/components/sidenav/examples

Full stackblitz example:

https://stackblitz.com/angular/qdpqnrlnpom?file=app%2Fsidenav-fixed-example.ts

Upvotes: 2

Nehal
Nehal

Reputation: 13307

To keep the toolbar on top, use position: fixed and z-index: 999. To keep the sidenav outside of scrollable area, it needs to be taken outside of md-sidenav-container

Thanks for creating a the plnkr demo, it helped a lot to quickly find a solution. Here's the modified demo.

app.component.html:

<md-toolbar class="fixed-header">
      <button class="app-icon-button" (click)="start.toggle()">
      <i class="material-icons app-toolbar-menu">menu</i>
      </button>
</md-toolbar>

<md-sidenav-container >
    <div class="text" style="min-height: 99vh">
      {{ text }}
    </div>
</md-sidenav-container>

<md-sidenav #start class="app-sidebar side-nav-style" mode="side" >
        test
</md-sidenav>

app.component.css:

.fixed-header {
  top: 0;
  position: fixed;
  z-index:999;

}

.text { 
  width: 5px;
}

.side-nav-style{
  background-color: grey;
  width: 6em;
  margin-top:62px;
  position: fixed
}

Let me know if that's what you are looking for.

Upvotes: 9

Related Questions