简体   繁体   English

如何使用 Angular CDK 菜单创建动态菜单

[英]How to create a dynamic menu using the Angular CDK Menu

I'm working on building an application menubar-style menu using the new Angular CDK Menu .我正在使用新的Angular CDK Menu构建应用程序菜单栏样式的菜单。 I want to dynamically build this menu from a given JSON structure rather than hardcoding the menu and each submenu as shown in the docs examples.我想从给定的 JSON 结构动态构建此菜单,而不是像文档示例中所示对菜单和每个子菜单进行硬编码。 Since I want to dynamically build a menu, I'm not sure how to do it since I can't dynamically create template reference variables that I can then reference with the menu trigger.由于我想动态构建一个菜单,我不知道该怎么做,因为我无法动态创建模板引用变量,然后我可以使用菜单触发器引用这些变量。

I've tried doing a recursive component method, but that doesn't fully work, as the menus don't close when mousing to the next menu.我试过做一个递归组件方法,但这并不完全有效,因为当鼠标移动到下一个菜单时菜单不会关闭。 I believe it's because once they're inside the component, the menu items are no longer direct siblings which makes it so they don't properly register in terms of auto closing when a sibling opens.我相信这是因为一旦它们进入组件内部,菜单项就不再是直接的兄弟姐妹,因此当兄弟姐妹打开时,它们无法在自动关闭方面正确注册。 I have a StackBlitz I've been working on that has my attempt and it shows the behavior of not closing the previous menu when mousing to another menu item.我有一个我一直在尝试的StackBlitz ,它显示了在将鼠标悬停到另一个菜单项时不关闭上一个菜单的行为。 I've read the menu docs multiple times trying to see if I missed anything, and the CDK Menu is so new (just released a few weeks ago) that there's not really anything on the internet in the way of example usage.我已经多次阅读菜单文档,想看看我是否遗漏了什么,而且 CDK 菜单非常新(几周前刚刚发布),互联网上没有任何示例用法。

Basically, what the StackBlitz shows, I have my menubar definition in the AppComponent defining the cdkMenuBar , like so:基本上,StackBlitz 显示的内容是,我在 AppComponent 中定义了cdkMenuBar的菜单栏定义,如下所示:

<div class="titlebar" cdkMenuBar>
  <app-menu *ngFor="let item of menu" [item]="item" [isRoot]="true"></app-menu>
</div>

Then I have the actual menu component which is used recursively, like so:然后我有递归使用的实际菜单组件,如下所示:

<button
  class="menu-item-button"
  cdkMenuItem
  [cdkMenuTriggerFor]="menu"
  [class.menubar-button]="isRoot"
>
  <div>{{ item.label }}</div>
  <div *ngIf="!isRoot && item.type === 'submenu'">&gt;</div>
</button>

<ng-template #menu>
  <div class="menu-dropdown" cdkMenu>
    <ng-container *ngFor="let subItem of item.submenu">
      <ng-container *ngIf="subItem.type !== 'separator'; else separator">
        <app-menu
          [item]="subItem"
          *ngIf="subItem.type === 'submenu'; else menuItem"
        ></app-menu>
        <ng-template #menuItem>
          <button class="menu-item-button" cdkMenuItem>
            <div>{{ subItem.label }}</div>
            <div class="text-color-tertiary">
              {{ subItem.accelerator }}
            </div>
          </button>
        </ng-template>
      </ng-container>
      <ng-template #separator>
        <hr />
      </ng-template>
    </ng-container>
  </div>
</ng-template>

That basically iterates through the "submenu" items in the menu template and then for each one invokes the menu component recursively.这基本上遍历菜单模板中的“子菜单”项,然后为每个项递归调用菜单组件。 Eventually there is no submenu and the recursion ends there.最终没有子菜单,递归在那里结束。

As I mentioned above, I believe the cause may be related to the menu items not being direct siblings which possibly makes it so the Angular CDK doesn't know to close those ones.正如我上面提到的,我认为原因可能与菜单项不是直接兄弟有关,这可能导致 Angular CDK 不知道关闭这些菜单项。 I am hoping there's a way to make this dynamic usecase work.我希望有一种方法可以使这个动态用例工作。

If you don't exactly follow the ng-template[#menu] > [cdkMenu] > [cdkMenuItem] hierarchy, the menu won't work as expected.如果您不完全遵循ng-template[#menu] > [cdkMenu] > [cdkMenuItem]层次结构,则菜单将无法按预期工作。

So you can't wrap [cdkMenu] or [cdkMenuItem] with app-menu without taking some steps to restore the required DOM structure.所以你不能用app-menu包装[cdkMenu][cdkMenuItem]而不采取一些步骤来恢复所需的 DOM 结构。

You could solve the nesting issue by directly referencing menuComponent.menu in the parent component's [cdkMenuTriggerFor] :您可以通过在父组件的[cdkMenuTriggerFor]中直接引用menuComponent.menu来解决嵌套问题:

app.component.html app.component.html

<button [cdkMenuTriggerFor]="menuComponent.menu">menu</button>

<app-menu #menuComponent [items]="menuItems"></app-menu>

menu.component.html menu.component.html

<ng-template #menu>
  <div cdkMenu>
    <ng-container *ngFor="let item of items">
      <ng-container *ngIf="hasChildren(item); else leafNode">
        <button cdkMenuItem [cdkMenuTriggerFor]="subMenu" *ngIf="menuComponent.menu as subMenu">
              <div>{{ item.label }}</div>
              <div *ngIf="item.children">▸</div>
            </button>
        <app-menu #menuComponent [items]="item.children!"></app-menu>
      </ng-container>
      <ng-template #leafNode>
        <button cdkMenuItem>
              {{ item.label }}
            </button>
      </ng-template>
    </ng-container>
  </div>
</ng-template>

Working demo: https://stackblitz.com/github/rensjaspers/cdk-dynamic-menu-demo?file=src/app/app.component.html工作演示: https://stackblitz.com/github/rensjaspers/cdk-dynamic-menu-demo?file=src/app/app.component.html

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM