4

I'm trying to create a mat-nav-list which has mat-expansion-panels as list-items. The problem is, the mat-nav-list doesn't work as expected.The alignments are a mess and the expansion panels do not work properly. As I think, this happens because the mat-nav-lists don't support mat-expansion-panels as list items.(Correct me if I'm wrong) enter image description here

My html code is as following

<mat-nav-list>
    <mat-list-item>
    <mat-expansion-panel>
      <mat-expansion-panel-header>
        <mat-panel-title>
          Personal data
        </mat-panel-title>
        <mat-panel-description>
          Type your name and age
        </mat-panel-description>
      </mat-expansion-panel-header>
      <mat-form-field>
        <input matInput placeholder="First name">
      </mat-form-field>
      <mat-form-field>
        <input matInput placeholder="Age">
      </mat-form-field>
    </mat-expansion-panel>
  </mat-list-item>

  <mat-list-item>
    <mat-expansion-panel (opened)="panelOpenState = true"
                         (closed)="panelOpenState = false">
      <mat-expansion-panel-header>
        <mat-panel-title>
          Self aware panel
        </mat-panel-title>
        <mat-panel-description>
          Currently I am {{panelOpenState ? 'open' : 'closed'}}
        </mat-panel-description>
      </mat-expansion-panel-header>
      <p>I'm visible because I am open</p>
    </mat-expansion-panel>
  </mat-list-item>
</mat-nav-list>

I will be thankful if someone can provide me a way to deal with this issue.

mruanova
  • 6,351
  • 6
  • 37
  • 55
Tharushi Geethma
  • 1,249
  • 15
  • 21

3 Answers3

4

Instead of using mat-list-item you can have a div on for loop is iterated and in that you can add the expansion panel likewise

<div *ngFor="let hero of herolist">
   <mat-expansion-panel>
    // here you can do whateever you want
   </mat-expansion-panel>
</div>

If you want to achieve it with sidenav, you can have it

<ng-container>
    <mat-side-nav>
    </mat-side-nav>
</ng-container>
Bart de Ruijter
  • 917
  • 10
  • 21
vishal
  • 137
  • 1
  • 7
0

Perhaps you want to nest the expansion panels in a mat-accordion?

<mat-accordion>
  <mat-expansion-panel></mat-expansion-panel>
  <mat-expansion-panel></mat-expansion-panel>
</mat-accordion>

https://material.angular.io/components/expansion/overview#accordion

bontragersw
  • 61
  • 1
  • 3
0

Without a lot of hacking mat-expansion-panel does not work properly with mat-nav-list. However, mat-tree does work nicely with it.

You can combine them as follows. Also see this Stackblitz example that I created.

HTML:

<mat-nav-list>
  <mat-tree [dataSource]="menuItems" [treeControl]="menuTreeControl">
    <mat-tree-node *matTreeNodeDef="let menuItem">
      <a
        [routerLink]="menuItem.link"
        mat-list-item
        routerLinkActive="mdc-list-item--activated"
      >
        {{ menuItem.name }}
      </a>
    </mat-tree-node>

    <mat-nested-tree-node *matTreeNodeDef="let menuItem; when: hasChild">
      <a mat-list-item matTreeNodeToggle>
        <span matListItemTitle
          >{{ menuItem.name }}
          <mat-icon>
            {{
              menuTreeControl.isExpanded(menuItem)
                ? "expand_more"
                : "expand_less"
            }}
          </mat-icon>
        </span>
      </a>
      <div *ngIf="menuTreeControl.isExpanded(menuItem)" role="group">
        <ng-container matTreeNodeOutlet></ng-container>
      </div>
    </mat-nested-tree-node>
  </mat-tree>
</mat-nav-list>

SCSS:

mat-nav-list {
  div[role="group"] {
    padding-left: 20px;
  }

  mat-icon {
    position: absolute;
    right: 20px;
  }
}

Typescript

export class NavMenuComponent {
  previewEnabled = Cookies.get("previewEnabled") === "true";
  title$: Observable<string>;

  menuTreeControl = new NestedTreeControl<MenuItem>(
    (menuItem) => menuItem.children
  );

  menuItems: MenuItem[] = [
    { type: "link", link: "link-1", name: "Item 1"},
    {
      type: "section",
      name: "Parent Item",
      children: [
        { type: "link", link: "link-2", name: "Item 2" },
        { type: "link", link: "link-3", name: "Item 3" },
      ],
    },
  ];

  constructor(
    private router: Router,
  ) {
    router.events
      .pipe(filter((event) => event instanceof NavigationEnd))
      .subscribe(() => this.openActiveRouteParentMenuItem());
  }

  openActiveRouteParentMenuItem() {
    _.forEach(this.menuItems, (parentMenuItem) => {
      const childMenuItems = this.menuTreeControl.getChildren(
        parentMenuItem
      ) as MenuItem[];
      _.forEach(childMenuItems, (childMenuItem) => {
        if (
          this.router.isActive(childMenuItem.link as string, {
            paths: "subset",
            queryParams: "subset",
            fragment: "ignored",
            matrixParams: "ignored",
          })
        ) {
          this.menuTreeControl.expand(parentMenuItem);
        }
      });
    });
  }
  
  hasChild = (_: number, menuItem: MenuItem) => !!menuItem.children?.length;
}

interface MenuItem {
  type: "link" | "section";
  name: string;
  link?: string;
  children?: MenuItem[];
}
Mark Lagendijk
  • 6,247
  • 2
  • 36
  • 24