Jeffrey Chen
Jeffrey Chen

Reputation: 1967

Angular sidenav.toggle() doesn't work after button reappear by *ngIf

This is the exact same code from Angular material website showing how to create different sidenav for mobile and desktop.

<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>

<div *ngIf="!shouldRun">Please open on Stackblitz to see result</div>

I want the menu button only appear when it is mobile. I add *ngIf="mobileQuery.matches" to it.

Change the 3rd line to:

<button mat-icon-button *ngIf="mobileQuery.matches" (click)="snav.toggle()"><mat-icon>menu</mat-icon></button>

Strangely, if you resize the window to make the button disappear then resize it to make the button appear again, the button won't work anymore. The sidenav will not show when you click it. Only when you resize the window again, the menu will suddenly appear.

You can test it here

Upvotes: 2

Views: 2950

Answers (4)

Fernando Flores
Fernando Flores

Reputation: 53

I had the same issue. After removing changeDetectorRef.detectChanges() the issue was gone.

Upvotes: 0

luixaviles
luixaviles

Reputation: 689

Since your main goal is to show/hide the menu button, I think you only need the following code into the constructor:

constructor(media: MediaMatcher) {
    this.mobileQuery = media.matchMedia('(max-width: 600px)');
}

Everything works fine after applying that change. Take a look at this stackblitz.

Let me know if that helps!

Upvotes: 2

Dean
Dean

Reputation: 2273

This is a strange one. It actually is a problem with change detection. If you manually trigger change detection after toggling the sidebar, everything works fine. Consider the following:

<!-- On click, call our toggle function which will toggle the side-nav and 
     trigger change detection -->
<button mat-icon-button *ngIf="mobileQuery.matches" (click)="toggle(snav)"><mat-icon>menu</mat-icon></button>

And our toggle function:

toggle(sidenav: any) {
  sidenav.toggle();
  this.changeDetectorRef.detectChanges();
}

Everything works as expected. Additionally, if you just show/hide the button (don't remove it from the DOM), everything works as expected:

<!-- As long as you only show/hide the button, everything works as expected -->
<div [hidden]="!mobileQuery.matches">
  <button mat-icon-button  (click)="snav.toggle()"><mat-icon>menu</mat-icon></button>
</div>

I have created a stackblitz that shows these examples working. This is really strange and I think you may have discovered a bug in Angular Material. Let me know if you are going to submit a bug request on the repo; if not, I will.

Upvotes: 3

bmtheo
bmtheo

Reputation: 1199

The click event works fine (you can add some logs to check it), it's actually the media query on the mat-sidenav tag (line 9) that breaks the snav.toggle()

Upvotes: -1

Related Questions