Grid component column performance

Hello,

I have problems with grid column rendered by vaadin components. Performance of this column is very poor, so I have found some suggestions to use TemplateRenderer instead of ComponentRenderer. Usage of TemplateRenderer is not so simple and now I have problem how to use MenuBar component. I’m able to create MenuItems based on pure texts but I don’t how to add icon to menu items. Main problem is that menu items defined in <vaadin-menu-bar items=[[item.menuItems] ]> don’t able to define icon in component properties. Component property can contains only HTML element name (which is not enough to define icon) or HTML element instance (which I’m not able to create in Java side) see [documentation]
(https://vaadin.com/components/vaadin-menu-bar/html-examples/menu-bar-items-demos).

I have also tried to put items through JavaScript but without success. It doesn’t work.

Can you help me and give me some another idea how to solve it and use vaadin menu bar component in TemplateRenderer?

Hello,

Can you share the ComponentRenderer you want to convert?

The most important part in the performance of the column is the content. If the component is “slow” then the column will be slow.

I’m not sure that a menu bar (1 “complex” component) that contains icons (1 component per icon) will have good performance (even) with TemplateRenderer.

It’s ComponentRenderer with MenuBar component:

ourGrid.addComponentColumn(gridItem -> {
		MenuBar menuBar = new MenuBar();
		menuBar.addThemeVariants(MenuBarVariant.LUMO_ICON);

		MenuItem btnMenu = menuBar.addItem(new Icon(VaadinIcon.MENU));


		MenuItem firstMenuItem = btnMenu.getSubMenu().addItem(getButton(VaadinIcon.FILE_TEXT_O, "Item 1"));
		MenuItem secondMenuItem = btnMenu.getSubMenu().addItem(getButton(VaadinIcon.FILE_TEXT_O, "Item 2"));
		MenuItem thirdMenuItem = btnMenu.getSubMenu().addItem(getButton(VaadinIcon.FILE_TEXT_O, "Item 3"));

		firstMenuItem.setEnabled(true);
		secondMenuItem.setEnabled(true);
		thirdMenuItem.setEnabled(true);

		return menuBar;
	})
	.setSortable(false)
	.setAutoWidth(true)
	.setFlexGrow(0);
}

...

private Component getButton(VaadinIcon icon, String label) {
	Button btn = ComponentHelper.getTertiaryButton(label);
	btn.setIcon(icon.create());
	return btn;
}

Without this column our grid is rendered about aprox. 1s, with this column it takes aprox. 4s (tested with browser and server on my localhost) what is really big difference (also for user experience). In some blog [Vaadin’s blog]
(http://https://vaadin.com/blog/using-the-right-r) I have seen that your grid has some performance problem with ComponentRenderer. There is suggestion to use TemplateRenderer. Problem is that I’m not able to create this menu with icons through TemplateRenderer.

Yes, the template render is quite hard to use in this case.

One solution is to create your own webcomponent that is using the menu item.
Basically a menu that contains only the minimum information (text, icon, children) and create the component.

Here is an example:


// add all the components that are used in the menuicon-lit-renderer
@Uses(MenuBar.class)
@Uses(Icon.class)
@Uses(Button.class)
@JsModule("./src/menuicon-lit-renderer.js")
@Route("template-grid")
public class GridView extends VerticalLayout {

    public GridView() {
        Grid<String> grid = new Grid<>();
        grid.setSelectionMode(Grid.SelectionMode.NONE);
        grid.addColumn(TemplateRenderer.<String> of("<menuicon-lit-renderer items=\"[[item.items]
]\"></menuicon-lit-renderer>").withProperty(
            "items", c -> {
                JsonArray rootMenuItems = Json.createArray();
                JsonObject rootMenuItem1 = Json.createObject();
                rootMenuItem1.put("icon", "vaadin:menu");
                rootMenuItems.set(0, rootMenuItem1);

                JsonArray children = Json.createArray();
                JsonObject submenu1 = Json.createObject();
                submenu1.put("icon", "vaadin:file-text-o");
                submenu1.put("text", "Item 1");
                children.set(0, submenu1);
                JsonObject submenu2 = Json.createObject();
                submenu2.put("icon", "vaadin:file-text-o");
                submenu2.put("text", "Item 2");
                children.set(1, submenu2);
                JsonObject submenu3 = Json.createObject();
                submenu3.put("icon", "vaadin:file-text-o");
                submenu3.put("text", "Item 3");
                children.set(2, submenu3);
                rootMenuItem1.put("children", children);

                return rootMenuItems;
            }))
            .setSortable(false)
            .setWidth("150px")
            .setFlexGrow(0);

       grid.addComponentColumn(gridItem -> {
            MenuBar menuBar = new MenuBar();
            menuBar.addThemeVariants(MenuBarVariant.LUMO_ICON);

            MenuItem btnMenu = menuBar.addItem(new Icon(VaadinIcon.MENU));


            MenuItem firstMenuItem = btnMenu.getSubMenu().addItem(getButton(VaadinIcon.FILE_TEXT_O, "Item 1"));
            MenuItem secondMenuItem = btnMenu.getSubMenu().addItem(getButton(VaadinIcon.FILE_TEXT_O, "Item 2"));
            MenuItem thirdMenuItem = btnMenu.getSubMenu().addItem(getButton(VaadinIcon.FILE_TEXT_O, "Item 3"));

            firstMenuItem.setEnabled(true);
            secondMenuItem.setEnabled(true);
            thirdMenuItem.setEnabled(true);

            return menuBar;
        })
            .setSortable(false)
            .setAutoWidth(true)
            .setFlexGrow(0);
        add(grid);

        List<String> strings = new ArrayList<>();
        for (int i = 0; i < 30; i++) {
            strings.add(i + "");
        }

        grid.setItems(strings);
        grid.setSizeFull();
        setSizeFull();
    }
    private Component getButton(VaadinIcon icon, String label) {
        Button btn = new Button(label);
        btn.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
        btn.setIcon(icon.create());
        return btn;
    }
}

And the component in javascript: (/src/menuicon-lit-renderer.js)

import { LitElement, html } from 'lit-element';
import '@vaadin/vaadin-menu-bar';

class MenuiconLitRenderer extends LitElement {
    static get properties() {
        return {
            items: { type: Array }
        };
    }

    constructor() {
        super();
    }

    firstUpdated() {
        this.items = [];
    }
    update(changedProperties) {
        super.update(changedProperties);
        if (changedProperties.has('items')) {
            this.menuitems = this.buildMenuicon();
            debugger;
        }
    }
    buildMenuicon() {
        const menuitems = [];
        this.items.forEach(item => {
            menuitems.push(this.convertItemToMenu(item));
        });
        return menuitems;
    }

    convertItemToMenu(item) {
        const children = []
        if (item.children) {
            item.children.forEach(child => {
                children.push(this.convertItemToMenu(child));
            });
        }
        return  { component: this.makeIcon(item.icon, item.text), children: children};
    }
    /*
    menu.addEventListener('item-selected', function(e) {
        document.querySelector('i').textContent = e.detail.value.text;
    });*/

    makeIcon(img, txt) {
        const item = window.document.createElement('vaadin-context-menu-item');
        const icon = window.document.createElement('iron-icon');
        icon.setAttribute('icon', img);
        item.appendChild(icon);
        if (txt) {
            item.appendChild(window.document.createTextNode(txt));
        }
        return item;
    }

    render() {
        return html`<vaadin-menu-bar id="menubar" .items="${this.menuitems}"></vaadin-menu-bar>`;
    }
}

customElements.define('menuicon-lit-renderer', MenuiconLitRenderer);

That should give you an idea of the difference of performance.
In my computer, the template render is twice faster than the component renderer but it’s less flexible.

Thank you for your suggestion but I have two questions about it:

  1. Is it compatible with vaadin 14?
  2. how to handle click event from some menu item on server?

About second point I see problem that event for TemplateRenderer defined in “on-item-selected” attribute of vaadin-menu-bar and by TemplateRenderer.withEventHandler propagate only Grid’s row item and I’m not able to detect which menu item from menubar was clicked/selected. How to solve this problem? The same question has been describe [here]
(https://github.com/vaadin/flow/issues/6266) but without answer. Can you give me any idea for this problem?

  1. Yes, the code is working in Vaadin 14.
  2. I don’t know. As you found it’s not easy to do. You can probably use a different component like a button+popup which could be created in Java when you click on the button. (https://vaadin.com/directory/component/popup/2.2.1/overview)