我正在尝试使用 Angular 下的 Clarity UI 库动态生成上下文菜单。我找不到'contextmenu' Clarity 组件,所以我正在使用该dropdown
组件 - 我意识到下拉菜单应该通过附加到按钮来工作,但我不希望它附加到按钮 - 我想要它作为右键菜单,如下所述。(抱歉这篇文章的长度,我只是想确保所有信息都在那里。)
我在这里遇到问题的 Stackblitz:https ://stackblitz.com/edit/angular-ivy-xsp8ik
在我的主要组件中,我有两个 SVG 圆圈,当我单击它们时,我想显示一个动态生成的上下文菜单(对于这个示例,我只需单击鼠标左键,在我的真实代码中,我有 JointJS 节点响应右键单击)。
主要组件创建“contextMenuInfo”结构,该结构包含鼠标位置信息(指定菜单应出现的位置)、指示应显示菜单的布尔值和指示单击哪个圆圈的节点 ID。
<div (mousemove)="onMouseMoved($event)">
<svg viewBox="0 0 300 200" xmlns="http://www.w3.org/2000/svg">
<circle cx="25" cy="25" r="20" (click)="itemClick(1)" />
<circle cx="75" cy="25" r="20" (click)="itemClick(2)" />
</svg>
</div>
<div style="position: absolute" [style.top]="contextMenuInfo.top" [style.left]="contextMenuInfo.left">
<contextmenu [showContextMenu]="contextMenuInfo.showContextMenu" [nodeId]="contextMenuInfo.nodeId"></contextmenu>
</div>
contextmenu 组件包含应该显示上下文菜单的 html。clr-dropdown-menu 中有三种类型的元素:alabel
创建一个菜单标题,一个item
应该产生一个带有点击回调的实际菜单按钮,一个submenu
应该产生一个嵌套的子菜单。该代码用于*ngFor
循环遍历菜单项,以及*ngIf
用于测试该项是哪种类型的语句,并将显示相应的内容。
<clr-dropdown class="contextdropdown" [clrCloseMenuOnItemClick]="true">
<clr-dropdown-menu *clrIfOpen="showContextMenu" class="contextmenudropdown">
<ng-container *ngFor="let item of menuItems()">
<label *ngIf="item.type === 'header'" class="dropdown-header">{{item.text}} for node {{item.id}}</label>
<button *ngIf="item.type === 'item'" type="button" clrDropdownItem
(click)="item.click(item.text)">{{item.text}} for node {{item.id}}</button>
<clr-dropdown *ngIf="item.type === 'submenu'">
<button clrDropdownTrigger>{{item.text}}</button>
<clr-dropdown-menu class="contextmenudropdown">
<ng-container *ngFor="let subitem of submenuItems(item.id, item.text)">
<label *ngIf="subitem.type === 'header'" class="dropdown-header">{{subitem.text}} for node
{{subitem.id}}</label>
<button *ngIf="subitem.type === 'item'" type="button" clrDropdownItem
(click)="subitem.click(subitem.text)">{{subitem.text}} for node {{subitem.id}}</button>
</ng-container>
</clr-dropdown-menu>
</clr-dropdown>
</ng-container>
</clr-dropdown-menu>
</clr-dropdown>
这是生成菜单内容的代码。它每 5 个菜单项任意创建子菜单。
menuItems() {
const list = [];
list.push(
{ type: 'header', text: 'Heading', id: this.nodeId }
);
for (let i = 0; i < 10; ++i) {
if (i % 5 === 4) {
list.push(
{ type: 'submenu', text: 'Submenu' + i, id: this.nodeId }
);
} else {
list.push(
{ type: 'item', text: 'MenuItem ' + i, id: this.nodeId, click: this.callback }
);
}
}
console.log('created menuItems list of size: ' + list.length);
return list;
}
submenuItems(nodeId: number, submenuId: number) {
const list = [];
list.push(
{ type: 'header', text: 'Submenu Heading for ' + submenuId, id: nodeId }
);
for (let si = 0; si < 5; ++si) {
list.push(
{ type: 'item', text: 'SubMenuItem ' + si, id: nodeId, click: this.callback }
);
}
console.log('created submenuItems list nodeId: ' + nodeId + ', submenuId: ' + submenuId + ' of size: ' + list.length);
return list;
}
callback(id: string){
console.log('item ' + id + ' clicked');
}
这些是我看到的问题:
在主组件中使用 showContextMenu 布尔值并不好,因为单击后关闭菜单时它不会被重置,这意味着圆圈上的每一秒“点击”都会被吸收 - 我不确定正确的方法是什么这样做是。
对于这个 stackblitz,上下文菜单实际上并没有显示出来。看起来它被触发显示,因为在 menuItems() 和 submenuItems() 例程中放置一个断点表明它们被调用,但实际上没有显示任何内容。我的实际代码似乎可以正常显示主菜单 - 抱歉,但我无法弄清楚为什么 stackblitz 不显示菜单,而我的实际代码确实如此。一旦为此提供了解决方案,则可能需要回答以下其余问题。
这是在我的实际原型应用程序中显示的菜单图片:
我不知道为什么,但是每次单击圆圈都会调用 menuItems() 例程两次。在我的实际代码中,我实际上看到,一旦显示菜单,menuItems() 例程就会被调用很多次,尤其是在将鼠标移入和移出上下文菜单本身时(菜单保持显示 - 只需将鼠标移开和移开离开)。有没有办法限制调用的次数?最终的应用程序可能会花费大量时间来填充菜单,我不希望它被如此频繁地调用。
当显示菜单时(在我的实际代码中),单击任何菜单
item
似乎确实会根据需要触发回调,但是整个上下文菜单并没有消失(它应该消失,因为clrCloseMenuOnItemClick
设置为 true)。单击
submenu
按钮不会使定义submenu
出现 - 它什么也不做。
谢谢,感谢您提供的任何帮助。