angularangular-materialangular2-templatemat-sidenavmat-drawer

How to save sidenav (mat-drawer) state to localstorage in Angular Material?


I have problem with mat-drawer in Angular Material. I want to save my state to localstorage but mat-drawer is always opened.

Sidenav have expanded part and closed expanded part when stay mat-icons without text. When you click on bottom button Hide, the sidenav is Close but after refresh page Sidenav is again expanded (but in the localstorage is close).

Looks like this:

Opened expanded mat drawer

Closed mat drawer

Sidenav is open but in local storage is closed

Do you know how resolve this thing? Thank you!

Source code:

app.component.html

<mat-drawer-container class="content-container" autosize >
 <mat-drawer
  mode="side"
  class="sidenav"
  disableClose="true"
  opened="true">
<div class="sidenav-container">
<mat-nav-list>
      <a class="nav-item nav-link" mat-list-item routerLink="/private/dashboard" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">
        <mat-icon style="padding-right:5px;"  matTooltipPosition="right" [matTooltipDisabled]="disabledTooltip">dashboard</mat-icon>
        <span *ngIf="isExpanded">Dashboard</span>
      </a>

      <a class="nav-item nav-link" mat-list-item routerLink="/private/companies" routerLinkActive="active">
        <mat-icon style="padding-right:5px;">stars</mat-icon>
        <span *ngIf="isExpanded">Subjects</span>
      </a>

      <a class="nav-item nav-link" mat-list-item routerLink="/private/users" routerLinkActive="active">
        <mat-icon style="padding-right:5px;">group</mat-icon>
        <span *ngIf="isExpanded">Users</span>
      </a>

      <a class="nav-item nav-link" mat-list-item routerLink="/private/api-customers" routerLinkActive="active">
        <mat-icon style="padding-right:5px;">beenhere</mat-icon>
        <span *ngIf="isExpanded">Clients</span>
      </a>

      <a class="nav-item nav-link" mat-list-item routerLink="/private/applications" routerLinkActive="active">
        <mat-icon style="padding-right:5px;">apps</mat-icon>
        <span *ngIf="isExpanded">Apps</span>
      </a>

      <a class="nav-item nav-link" mat-list-item routerLink="/private/calendar" routerLinkActive="active">
        <mat-icon style="padding-right:5px;">calendar_today</mat-icon>
        <span *ngIf="isExpanded">Calendar</span>
      </a>
</mat-nav-list>
        <div class="sidenav-bottom">
          <mat-nav-list>
            <a class="nav-item nav-link" mat-list-item (click)="onSidenavToggle()">
              <mat-icon *ngIf="isExpanded">arrow_back_ios</mat-icon>
              <mat-icon *ngIf="!isExpanded">arrow_forward_ios</mat-icon>
              <span *ngIf="isExpanded">Hide</span>
            </a>
          </mat-nav-list>
        </div>
    </mat-drawer>
<mat-drawer-content>
Page content
</mat-drawer-content>
</mat-drawer-container>

app.component.ts

import {Component, HostListener, Inject, OnInit} from '@angular/core';
import { Router } from '@angular/router';
import {Subject} from 'rxjs';

@Component({
  selector: 'app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})

export class AppComponent  {

  isExpanded = true;
  constructor(
  private sidenavService: SidebarService) {
  this.sidenavService.setHideSidebar(this.sidenavService.isHideSidebar());
  }
  onSidenavToggle() {
    this.isExpanded = !this.isExpanded;
    this.sidenavService.setHideSidebar(!this.sidenavService.isHideSidebar());
    this.sidenavService.sideBarState$.next(this.isExpanded);
  }}

sidebar.service.ts

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';


@Injectable()
export class SidebarService {

  public sideBarState$: Subject<boolean> = new Subject<boolean>();

  constructor() {
    const initialValue = localStorage.getItem('isExpanded');
    this.sideBarState$.next(initialValue === 'close');
  }

  getHideSidebar(): Subject<boolean> {
    return this.sideBarState$;
  }

  setHideSidebar(newValue: boolean): void {
    localStorage.setItem('isExpanded', newValue ? 'close' : 'open');
    this.sideBarState$.next(newValue);
  }

  isHideSidebar(): boolean {
    return localStorage.getItem('isExpanded') === 'close';
  }

}

Solution

  • Turns out the problem is with your isExpanded in the component. The value is being hard coded to true and has nothing to do with the service (or the value stored in LOCALSTORAGE).

    Some other suggestions are to use the Subject value alone as the one true source of what isExpanded should be (Use the EMIT value, do not set it up yourself). Also it would make more sense if you store isExpanded as true or false, instead of open or close. The variable name suggests the value to be a true or false.

    import {Component, HostListener, Inject, OnInit} from '@angular/core';
    import { Router } from '@angular/router';
    import {Subject} from 'rxjs';
    
    @Component({
      selector: 'app',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.scss'],
    })
    
    export class AppComponent  {
    
      isExpanded;
      constructor(
      private sidenavService: SidebarService) {
      isExpanded = !this.sidenavService.isHideSidebar();
      }
      onSidenavToggle() {
        this.isExpanded = !this.isExpanded;
        this.sidenavService.setHideSidebar(!this.sidenavService.isHideSidebar());
        this.sidenavService.sideBarState$.next(this.isExpanded);
      }}