Rodrigo Batista
Rodrigo Batista

Reputation: 19

Angular with Angular Material sidenav and NgRx

I'm trying to use NgRx with the angular material, to open and close the sidenav, but I'm not maintaining it.

My Store:

export const sidenavFeatureKey = 'sidenav';

export interface SidenavState {
  status: boolean
}

export const initialSidenavState: SidenavState = {
  status: false
}

export const sidenavReducer = createReducer(
  initialSidenavState,
  on(toggleSideNav,
    (state) => {
      return {
        ...state,
        status: !state.status
      }
    }
  )
)

my action

export const toggleSideNav = createAction(
  "[Toolbar - Sidenav] Change sidenav to true or false"
)

my header

export class HeaderComponent {
  constructor(private sidenavStore: Store<SidenavState>) {
  }

  toggleSidenav() {
    this.sidenavStore.dispatch(toggleSideNav())
  }
}

<mat-toolbar absolute>
  <button (click)="toggleSidenav()" mat-icon-button>
    <mat-icon>menu</mat-icon>
  </button>
  <span>My App</span>
  <span class="example-spacer"></span>
  <button aria-label="Example icon-button with heart icon" class="example-icon favorite-icon" mat-icon-button>
    <mat-icon>favorite</mat-icon>
  </button>
  <button aria-label="Example icon-button with share icon" class="example-icon" mat-icon-button>
    <mat-icon>share</mat-icon>
  </button>
</mat-toolbar>

and my sidenav

export class SidenavComponent implements OnInit {
  sidenavStatus: boolean = true;

  constructor(private sidenavStore: Store<SidenavState>) {
  };

  ngOnInit() {
    this.sidenavStore.select("status").subscribe(
      toggle => this.sidenavStatus = toggle
    );
  }
}

<mat-sidenav-container>
  <mat-sidenav [(opened)]="sidenavStatus" mode="side">
    <p>Hiding</p>
  </mat-sidenav>
  <mat-sidenav-content>
    <p>Hello</p>
  </mat-sidenav-content>
</mat-sidenav-container>

My Store status, changes from true to false when clicking on the header button, but the sidenav does not open and close.

enter image description here

enter image description here

Upvotes: 0

Views: 306

Answers (1)

nate-kumar
nate-kumar

Reputation: 1771

This may be unrelatd, but you don't need two-way binding on opened, as sidenavStatus will be updated via your selector and the actions you emit. You could replace [(opened)] with [opened] as follows:

sidenav.html

<mat-sidenav-container>
  <mat-sidenav [opened]="sidenavStatus" mode="side">
    <p>Hiding</p>
  </mat-sidenav>
  <mat-sidenav-content>
    <p>Hello</p>
  </mat-sidenav-content>
</mat-sidenav-container>

This would then guarantee that the only thing responsible for controlling the state of your sidebar is the sidenavStatus variable in the parent component (unless of course you have logic in your mat-sidenav component which is changing the value of opened internally).


We could take this one step further and replace sidenavStatus with an Observable that contains only the toggle state you are interested in and then use this in the template with the async pipe

sidenav.ts

export class SidenavComponent implements OnInit {
  sidenavStatus$: Observable<boolean>;

  constructor(private sidenavStore: Store<SidenavState>) {
  };

  ngOnInit() {
    this.sidenavStatus$ = this.sidenavStore.select("status");
  }
}

sidenav.html

<mat-sidenav-container>
  <mat-sidenav [opened]="sidenavStatus$ | async" mode="side">
    <p>Hiding</p>
  </mat-sidenav>
  <mat-sidenav-content>
    <p>Hello</p>
  </mat-sidenav-content>
</mat-sidenav-container>

Now, whatever value resides in the store for the selector "status" will be fed directly into your child component. If it still doesn't work, you can then at least rule out the store implementation as the source of the issue

Upvotes: 0

Related Questions