Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you create a nested, collapsing menu with Angular Material2?

I am looking for support within Angular Material2 for nested menus within a sidebar. The top level would typically be closed by default, and opening a top level would expose nested menu items.

I thought this made sense as a starting point, but the child nav items render (poorly) outside of the parent items:

plnkr

<md-sidenav-container class="my-container">
  <md-sidenav #sidenav class="my-sidenav">
    <md-list>
        <md-list-item>
          <h3 md-line> First Parent </h3>
          <md-nav-list>
            <a md-list-item href="#">First Child</a>
            <a md-list-item href="#">Second Child</a>
            <a md-list-item href="#">Third Child</a>
          </md-nav-list>
        </md-list-item>
        <md-list-item>
          <h3 md-line> Second Parent </h3>
          <md-nav-list>
            <a md-list-item href="#">First Child</a>
            <a md-list-item href="#">Second Child</a>
          </md-nav-list>
        </md-list-item>
    </md-list>
  </md-sidenav>  
  <div class="my-container">
    <button md-button (click)="sidenav.open()">Open</button>
  </div>
</md-sidenav-container>

Has anyone created this kind of sidebar menu with @angular/material?

like image 717
Stuart Avatar asked Apr 02 '17 22:04

Stuart


1 Answers

I know this is an old question, but for others coming upon this page looking for the same thing, as I did, here's how I took care of it with the current version of Angular Material (6.4.6) and once you get the CSS styles done right, it works beautifully.

Please note that there still is no official support for this functionality, and you have to set it up yourself, which could be done a number of ways, but I chose to use only Angular Material components.

Here's example markup with some comments, using an object for your nav links:

<mat-sidenav-container>
  <mat-sidenav #sidenav
    class="sidenav"
    [mode]="mobileQuery.matches ? 'over' : 'side'"
    [opened]="mobileQuery.matches ? false : true">
    <mat-nav-list>
      <!-- wrap all the nav items in an accordion panel -->
      <mat-accordion [displayMode]="flat">
        <div *ngFor="let navItem of navList">

          <!-- use a simple div for an item that has no children,
            match up the styling to the expansion panel styles -->
          <div class="nav-head" *ngIf="navItem.pages.length === 0">
            <a class="nav-link"
              [routerLink]="navItem.link"
              routerLinkActive="selected"
              (click)="closeSidenav()">
              <mat-icon>{{navItem.icon}}</mat-icon>
              <span class="nav-link-text">{{navItem.heading}}</span>
            </a>
          </div>

          <!-- use expansion panel for heading item with sub page links -->
          <mat-expansion-panel *ngIf="navItem.pages.length > 0"
            class="mat-elevation-z0">
            <mat-expansion-panel-header class="nav-head" [expandedHeight]="'48px'">
              <mat-panel-title class="nav-link">
                <mat-icon>{{navItem.icon}}</mat-icon>
                <span class="nav-link-text">{{navItem.heading}}</span>
              </mat-panel-title>
            </mat-expansion-panel-header>
      
            <div class="nav-section">
              <!-- loop through all your sub pages inside the expansion panel content -->
              <div *ngFor="let navPage of navItem.pages"
                class="nav-item">
                <a class="nav-link"
                  [routerLink]="navPage.link"
                  routerLinkActive="selected"
                  (click)="closeSidenav()">{{navPage.title}}</a>
              </div>
            </div>
          </mat-expansion-panel>
        </div>
      </mat-accordion>
    </mat-nav-list>
  </mat-sidenav>
  <mat-sidenav-content>
    <div class="container-fluid">
      <router-outlet></router-outlet>
    </div>
  </mat-sidenav-content>
</mat-sidenav-container>

EDIT: To answer a couple of additional questions that were made, mobileQuery is coming from the Angular Material CDK which adds some helpers for detecting mobile breakpoints inside your components. See here: Angular Material CDK Layout

Also, my component file really doesn't do anything in this case other than pull the correct nav object to be displayed from a service, but here is an example of how I set up the objects (of course they could be whatever you need them to be)

[
  {
    heading: 'Dashboard',
    icon: 'dashboard',
    link: '/dashboard',
    pages: []
  },
  {
    heading: 'Main Heading',
    icon: 'settings',
    link: '/settings',
    pages: [
      {
        title: 'Subpage',
        link: '/settings/advanced',
        icon: ''
      }
    ]
  }
]
like image 99
Aaron Hazelton Avatar answered Oct 04 '22 23:10

Aaron Hazelton