5
votes

I have an Angular 8 project with Angular Material 8 that cannot toggle the sidenav via a button in the same component. The javascript console says that

cannot read property 'toggle' of undefined

but the sidenav has the reference variable in the template.

I have made sure the button is in the template so that I don't have to worry about event emitters or other trickery. This should be straightforward.

HTML

<mat-toolbar color="primary">
  <button mat-icon-button (click)="sidenav.toggle()" *ngIf="(isHandset$ | async)"><mat-icon>menu</mat-icon></button>
  <span>This Be Me App, Matey</span>
</mat-toolbar>
<mat-sidenav-container class="sidenav-container">
  <mat-sidenav #sidenav class="sidenav" fixedInViewport="true" *ngIf="isAuthenticated()"
      [attr.role]="(isHandset$ | async) ? 'dialog' : 'navigation'"
      [mode]="(isHandset$ | async) ? 'over' : 'side'"
      [opened]="(isHandset$ | async) ? 'false' : 'true'">
    <mat-toolbar><mat-icon (click)="sidenav.toggle()" *ngIf="(isHandset$ | async)">arrow_back</mat-icon> Menu</mat-toolbar>
    <mat-nav-list>
      <a mat-list-item [routerLink]="['/home']" routerLinkActive="active">Home</a>
      <a mat-list-item (click)="logout()">Logout</a>
    </mat-nav-list>
  </mat-sidenav>
  <mat-sidenav-content class="content">    
    <!-- Add Content Here -->
    <router-outlet></router-outlet>
  </mat-sidenav-content>
</mat-sidenav-container>

The menu button appears and disappears at the breakpoint like expected, but clicking it gives the error.

What should happen is that the button only appears when you get below the isHandset$ break (which happens) and then it should open over the top of the content (which doesn't happen). Since the #sidenav reference variable is in the mat-sidenav tag it seems like I've got it set up properly.

1
Can you throw this in a stackblitz? - Jacques ジャック
Is your isAuthenticated() true by the time you try to call sidenav.toggle()? - Jojofoulk
@Jojofoulk yes. That method looks for a token in local storage and checks the expiration date. If it's not expired, then isAuthenticated() = true. I've checked that in the Chrome debugger. I'll get it up on Stackblitz. - DeerSteak
Ugh. Aside from the icons not displaying on stackblitz, the example works. :-/ stackblitz.com/edit/angular-vppsa3 - DeerSteak

1 Answers

2
votes

the error you shared must have a dependency or something missing; to see the working behavior, we had to address the 2 *ngIf conditions in your code:

  • isAuthenticated() -- which could be false, we don't know the logic you had used
  • (isHandset$ | async) -- we don't know the observable logic

assuming these 2 *ngIf conditions to be true, the code works fine... no error of 'cannot read property 'toggle' of undefined'

relevant ts:

import {Component} from '@angular/core';

@Component({
  selector: 'sidenav-open-close-example',
  templateUrl: 'sidenav-open-close-example.html',
  styleUrls: ['sidenav-open-close-example.css'],
})
export class SidenavOpenCloseExample {
  events: string[] = [];
  opened: boolean;
  isHandset$:boolean =true;
  isAuthenticated(){ return true;}

  shouldRun = [/(^|\.)plnkr\.co$/, /(^|\.)stackblitz\.io$/].some(h => h.test(window.location.host));
}

you can check working stackblitz here