如何使用Angular Material创建折叠菜单?

7

在Angular Material的文档中有一个很酷的功能。可以展开/折叠子类别的菜单。

是否可能使用某个组件创建它?还是我必须从头开始自己做?或者您可以建议一些可以节省时间的软件包。

enter image description here


Angular Material网站使用的是Angular CDK版本的Angular Material扩展面板组件加上一些CSS。 - Edric
4个回答

7

我曾尝试使用侧边导航来实现这一点。也许可以为您节省一些时间。

导航项遵循一个简单的接口:

interface NavItem {
displayName: string;
disabled?: boolean;
iconName: string;
route?: string;
children?: NavItem[];}

然后在component.html中,主要思路是递归添加nav-items(*ngFor)。对于每个类别使用mat-accordions,并再次使用*ngFor获取其子级:

<mat-nav-list>
<span *ngFor="let item of menu">
  <span *ngIf="item.children && item.children.length > 0">
    <mat-accordion>
      <mat-expansion-panel>
        <mat-expansion-panel-header>
          <mat-panel-title>
            <!-- Cabeceras del submenu -->
            <div fxLayout="row" fxLayoutAlign="space-between center" > 
              <mat-icon>{{item.iconName}}</mat-icon>
              {{item.displayName}}
            </div>
          </mat-panel-title>
        </mat-expansion-panel-header>
        <span *ngFor="let child of item.children">
          <mat-list-item routerLink="[child.route]">
            <!-- Entradas de cada submenú  -->
            <div fxLayout="row" fxLayoutAlign="space-between center" > 
              <mat-icon>{{child.iconName}}</mat-icon>
              {{child.displayName}}
            </div>
          </mat-list-item>
        </span>
      </mat-expansion-panel>
    </mat-accordion>
  </span>
  <span *ngIf="!item.children || item.children.length === 0">
    <mat-list-item routerLink="[item.route]">
      <!-- Entradas principales -->
      <div fxLayout="row" fxLayoutAlign="space-between center">
        <mat-icon>{{item.iconName}}</mat-icon>
        {{item.displayName}}
      </div>
    </mat-list-item>
  </span>
</span>

请看:https://stackblitz.com/edit/angular-side-nav-dynamic-expansive-menu

这是一个不错的开始。但你能再进一步吗?例如,当我打开第二个多级菜单时,应该关闭第一个打开的菜单,并且需要为点击的菜单项提供颜色。我希望这对你来说是一个简单的任务。 - Sampath

4
mat-expansion-panel 放在 mat-accordion 中,把 mat-nav-list 放在扩展面板中。接着为新的导航手风琴创建全局样式。 StackzBlitz
<mat-accordion class="app-nav-accordion">
  <mat-expansion-panel class="mat-elevation-z0">
    <mat-expansion-panel-header>
      <mat-panel-title>Section Two</mat-panel-title>
    </mat-expansion-panel-header>
    <mat-nav-list>
      <a mat-list-item>Item Three</a>
        <a mat-list-item>Item Four</a>
    </mat-nav-list>
  </mat-expansion-panel>
</mat-accordion>

.app-nav-accordion {
  .mat-expansion-panel {
    border-radius: 0px !important;
    box-shadow: none !important;

    &.mat-expansion-panel-spacing {
      margin: 0px;
    }

    .mat-expansion-panel-body {
      padding: 0px;
    }

    .mat-expansion-panel-header {
      height: 40px;
      padding: 0px 24px 0px 16px;
    }

    .mat-expansion-panel-header-title {
      color: rgba(0,0,0,0.54);
      font-size: 14px;
      font-weight: 500;
    }
  }
}

0

使用mat-expansion-panel会是一个不错的开始。默认示例使用手风琴来仅展开一个面板,您可以在这里找到如何同时展开多个面板的示例:

1- 您应该删除mat-accordion以启用多个展开面板。

2- 使用expanded参数同时更改多个状态。


0
你可以结合mat-sidenavmat-tree来实现这个功能。而且这可以在不使用任何“hacky”修复的情况下完成,如果你试图使用mat-accordion来代替它,则会需要一些修复。
我创建了一个 Stackblitz示例,展示了结果。
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;
  }
}

组件:

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[];
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接