in the “Context Menu” documentation, there’s also an example how to add a Menu Button to a Grid. Both examples (Context Menu as well as Menu Button) leave me with a strange feeling because they all create all menu items in advance. Having, for example, 50 Grid items on screen with 10 menu items each would lead to creating 500 menu items just for the case that the user selects a single one of them.
I started googling around if I’m misunderstanding something. Most hits were about older Vaadin releases, but I found this: Callback before opening submenu · Issue #1184 · vaadin/flow-components · GitHub which sounds that there currently isn’t a solution. But most of this discussions are talking about complicated things like menu open listeners.
Wouldn’t it be the simplest way to have a normal button and build the menu on click?
But unfortunately, I did also not find a possibility to programmatically create and show a pop up menu.
How do you deal with that? Should I simply not worry about populating hundreds of menu items in advance? Or is there a way to programmatically open a pop up menu in a Button on click handler?
Well I can easily see in the debugger that the callback is called eagerly for all visible elements. So at least the server part is performance critical. But of course this can easily be dealed with now that I’m aware of that, and having some hundred unused menu items laying around server side, while still weird, shouldn’t be much of a problem.
But what you want to say is that although it is generated eagerly on server side for all (at least all shown) items, it is only transferred to the client as soon as needed? I think I will have to F12 this a bit. Thank you for your hint!
Hm, no, unfortunately you seem to be wrong. In the Chrome network monitor, I can see that the document which is transferred to the client, grows for about 100K depending on the menu items (40K for the grid alone, 140K for the grid with menu column). And in this specific JSON file which is transferred to the client, I can eagerly see all the menu items.
After a few more experiments I have to clarify: the big overhead results when I add a Menu column to the grid. If I instead simply add a GridContextMenu, there is no measurable overhead.
However, both examples are explained on the same page of documentation (on the Context Menu page), and on this very page there is the recommendation to prefer Menu Button over Context Menu.
… and it seems that a GridContextMenu cannot be dynamically populated depending on the item, while a Menu Button Column can.
I personally would also prefer a Menu Button over Context Menu, but with this big overhead, I will stay with the Context Menu for the moment since I currently do not need to populate the items dynamically.
setDynamicContentHandler (called before menu is opened, changes will be sent to client)
addGridContextMenuOpenedListener (called after menu is opened in client)
Just setup the content of the item on each right click. We do something like this:
Registration filterItemClickReg = null;
public setup() {
var test = new GridContextMenu<Bo>();
var editItem = test.addItem("Edit");
var filterItem = test.addItem("Column Filter");
test.setDynamicContentHandler(object -> {
editItem.setEnabled(object != null);
Column<Bo> column = null; // getting the column here can i post on another day
filterItem.setText("Add filter for column " + column.getHeaderText());
if (filterItemClickReg != null) filterItemClickReg.remove();
filterItemClickReg = filterItem.addMenuItemClickListener(micEvent-> {});
// return true, when the context menu should be opened
return object != null;
});
test.addGridContextMenuOpenedListener(gcmoEvent -> {
Optional<String> clickedColumn = gcmoEvent.getColumnId();
Optional<Bo> clickedItem = gcmoEvent.getItem();
});
}
Our menu button column:
actionColumn = this.grid.addColumn(MenuIconLitRenderer.<Bo>create());
...
public static class MenuIconLitRenderer {
public static <SOURCE> LitRenderer<SOURCE> create() {
return LitRenderer.<SOURCE>of("<div class=\"action-column-wrapper\">"
+ "<vaadin-icon part=\"icon\" icon=\"fas:bars\" onclick=\"window.triggerGridRightClick(event)\"></vaadin-icon>"
+ "</div>");
}
}
...
// execute js in client once. This makes a left click to a right click event.
if (!window.triggerGridRightClick) {
window.triggerGridRightClick = function(cEvent) {
if (cEvent && cEvent.button == 0) {
cEvent.target.dispatchEvent(new MouseEvent('contextmenu', { 'view': window, 'bubbles': true, 'cancelable': true }));
}
}
}