Page 2 of 2

Re: Cache / Avoid request to database for each page request

Posted: 07 May 2021, 15:04
by rider
Great. many many thanks!

Just to have a timline for me... Do you think you can implement this in the next week or are we talking about more weeks or month?

Re: Cache / Avoid request to database for each page request

Posted: 07 May 2021, 17:14
by siris
Hi,

We are currently in a release cycle with template updates for PrimeFaces 10. And it takes a few more months to complete, as material themes will be written from scratch. For this reason, we can add such feature requests to the next release cycle, except for bugfix.
But I would like to solve your issue with p:cache. Please edit the tag folder as you see the error and mail it to me.

Best Regards,

Re: Cache / Avoid request to database for each page request

Posted: 11 May 2021, 09:32
by vished
Yes, this would be very great...
Can you let me know which time frame do we have for the release cycle?

Re: Cache / Avoid request to database for each page request

Posted: 11 May 2021, 10:10
by siris
Hi,

I can say the second half of the year, but I cannot give a clear date. It will be determined in the coming months.

Best Regards,

Re: Cache / Avoid request to database for each page request

Posted: 11 May 2021, 10:51
by vished
ok, great... so you add this in your backlog / github, right?

Re: Cache / Avoid request to database for each page request

Posted: 11 May 2021, 11:27
by siris
Hi,

Definitely yes. We created a card for this issue in Trello and an issue in Github. We do this all the time. We try not to miss anything. Do not worry.

Best Regards,

Re: Cache / Avoid request to database for each page request

Posted: 08 Sep 2021, 09:19
by vished
siris wrote:
11 May 2021, 11:27
Hi,

Definitely yes. We created a card for this issue in Trello and an issue in Github. We do this all the time. We try not to miss anything. Do not worry.

Best Regards,
Any feedback on this?

Re: Cache / Avoid request to database for each page request

Posted: 10 Sep 2021, 10:02
by siris
Hi,

We aim to release PrimeFaces 11 in the coming weeks. We will fix it in the next major template updates.

Best Regards,

Re: Cache / Avoid request to database for each page request

Posted: 30 Sep 2021, 13:16
by mert.sincan
Hi,

Structurally, adding a dynamic tabMenu to Morpheus limits the menu's customizability. Therefore, an additional intervention to Morpheus may not be welcomed by users. So I made some changes for you. You can integrate these changes into your project.

under org.primefaces.morpheus.component

Code: Select all

// TabMenu.java
@ListenerFor(sourceClass = TabMenu.class, systemEventClass = PostAddToViewEvent.class)
public class TabMenu extends UITabPanel implements org.primefaces.component.api.Widget, ClientBehaviorHolder, PrimeClientBehaviorHolder {

...
    public enum PropertyKeys {

        widgetVar, activeIndex, stateful, model;

        String toString;

        PropertyKeys(String toString) {
            this.toString = toString;
        }

        PropertyKeys() {
        }

        public String toString() {
            return ((this.toString != null) ? this.toString : super.toString());
        }
    }
    
    ....
    
    public List getModel() {
        return (java.util.List) getStateHelper().eval(PropertyKeys.model, null);
    }

    public void setModel(List<TabMenuItem> _model) {
        getStateHelper().put(PropertyKeys.model, _model);
    }
}
- Create this file,

Code: Select all

// TabMenuItem
/*
   Copyright 2009-2021 PrimeTek.

   Licensed under PrimeFaces Commercial License, Version 1.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

   Licensed under PrimeFaces Commercial License, Version 1.0 (the "License");

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
package org.primefaces.morpheus.component;

import java.io.Serializable;
import org.primefaces.model.menu.MenuModel;

public class TabMenuItem implements Serializable {

    private static final long serialVersionUID = 1L;

    private String id;
    private String icon;
    private String title;
    private MenuModel model;

    /**
     * Creates a new menu item without value.
     */
    public TabMenuItem() {
        // NOOP
    }

    public TabMenuItem(String id, String icon, String title) {
        this.id = id;
        this.icon = icon;
        this.title = title;
    }

    public TabMenuItem(String id, String icon, String title, MenuModel model) {
        this(id, icon, title);
        this.model = model;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getIcon() {
        return icon;
    }

    public void setIcon(String icon) {
        this.icon = icon;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public MenuModel getModel() {
        return model;
    }

    public void setModel(MenuModel model) {
        this.model = model;
    }
}

- New TabMenuRenderer.java

Code: Select all

/*
   Copyright 2009-2021 PrimeTek.

   Licensed under PrimeFaces Commercial License, Version 1.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

   Licensed under PrimeFaces Commercial License, Version 1.0 (the "License");

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
package org.primefaces.morpheus.component;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.faces.FacesException;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import org.primefaces.component.api.AjaxSource;
import org.primefaces.component.api.UIOutcomeTarget;
import org.primefaces.component.menuitem.UIMenuItem;
import org.primefaces.component.submenu.UISubmenu;
import org.primefaces.expression.SearchExpressionFacade;
import org.primefaces.model.menu.MenuElement;
import org.primefaces.model.menu.MenuItem;
import org.primefaces.model.menu.MenuModel;
import org.primefaces.model.menu.Separator;
import org.primefaces.model.menu.Submenu;
import org.primefaces.renderkit.OutcomeTargetRenderer;
import org.primefaces.util.AjaxRequestBuilder;
import org.primefaces.util.ComponentTraversalUtils;
import org.primefaces.util.WidgetBuilder;

public class TabMenuRenderer extends OutcomeTargetRenderer {

    @Override
    public void decode(FacesContext context, UIComponent component) {
        decodeBehaviors(context, component);
    }

    @Override
    public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
        TabMenu tabMenu = (TabMenu) component;

        if (tabMenu.getModel() != null) {
            encodeModel(context, component);
        }
        else {
            encodeDefault(context, component);
        }

        encodeScript(context, component);
    }

    public void encodeModel(FacesContext context, UIComponent component) throws IOException {
        TabMenu tabMenu = (TabMenu) component;
        ResponseWriter writer = context.getResponseWriter();
        List<TabMenuItem> model = tabMenu.getModel();
        String clientId = tabMenu.getClientId(context);

        writer.startElement("div", tabMenu);
        writer.writeAttribute("id", clientId, "id");
        writer.writeAttribute("class", "layout-tabmenu", null);

        writer.startElement("ul", tabMenu);
        writer.writeAttribute("class", "layout-tabmenu-nav", null);

        for (TabMenuItem menuitem : model) {
            writer.startElement("li", null);
            writer.writeAttribute("id", menuitem.getId(), "id");
            writer.startElement("a", null);
            writer.writeAttribute("href", "#", null);
            writer.startElement("i", null);
            writer.writeAttribute("class", menuitem.getIcon(), null);
            writer.endElement("i");

            writer.startElement("div", null);
            writer.writeAttribute("class", "layout-tabmenu-tooltip", null);
            writer.startElement("div", null);
            writer.writeAttribute("class", "layout-tabmenu-tooltip-arrow", null);
            writer.endElement("div");
            writer.startElement("div", null);
            writer.writeAttribute("class", "layout-tabmenu-tooltip-text", null);
            writer.writeText(menuitem.getTitle(), null);
            writer.endElement("div");
            writer.endElement("div");
            writer.endElement("a");
            writer.endElement("li");
        }

        writer.endElement("ul");

        writer.startElement("div", tabMenu);
        writer.writeAttribute("class", "layout-tabmenu-contents", null);
        for (int i = 0; i < model.size(); i++) {
            TabMenuItem menuitem = model.get(i);

            writer.startElement("div", null);
            writer.writeAttribute("class", "layout-tabmenu-content", null);

            writer.startElement("div", null);
            writer.writeAttribute("class", "layout-submenu-title clearfix", null);
            writer.startElement("span", null);
            writer.writeText(menuitem.getTitle(), null);
            writer.endElement("span");
            writer.endElement("div");

            writer.startElement("div", tabMenu);
            writer.writeAttribute("class", "layout-submenu-content", null);
            writer.startElement("div", null);
            writer.writeAttribute("class", "menu-scroll-content", null);

            MenuModel menuModel = menuitem.getModel();
            if (menuModel != null && menuModel.getElements() != null) {
                String id = menuitem.getId() + "_" + i;

                writer.startElement("ul", tabMenu);
                writer.writeAttribute("id", id, "id");
                writer.writeAttribute("class", "navigation-menu", null);
                encodeElements(context, tabMenu, menuModel.getElements());
                writer.endElement("ul");

                WidgetBuilder wb = getWidgetBuilder(context);
                wb.init("MorpheusMenu", "widget_" + id, id).finish();
            }

            writer.endElement("div");
            writer.endElement("div");

            writer.endElement("div");
        }
        writer.endElement("div");

        writer.endElement("div");
    }

    public void encodeDefault(FacesContext context, UIComponent component) throws IOException {
        TabMenu tabMenu = (TabMenu) component;
        ResponseWriter writer = context.getResponseWriter();
        List<UIComponent> children = tabMenu.getChildren();
        String clientId = tabMenu.getClientId(context);

        writer.startElement("div", tabMenu);
        writer.writeAttribute("id", clientId, "id");
        writer.writeAttribute("class", "layout-tabmenu", null);

        writer.startElement("ul", tabMenu);
        writer.writeAttribute("class", "layout-tabmenu-nav", null);

        for (UIComponent child : children) {
            if (child.isRendered() && child instanceof Tab) {
                Tab tab = (Tab) child;
                writer.startElement("li", tab);
                writer.writeAttribute("id", tab.getClientId(context), "id");
                writer.startElement("a", tab);
                writer.writeAttribute("href", "#", null);
                writer.startElement("i", tab);
                writer.writeAttribute("class", tab.getIcon(), null);
                writer.endElement("i");

                writer.startElement("div", null);
                writer.writeAttribute("class", "layout-tabmenu-tooltip", null);
                writer.startElement("div", null);
                writer.writeAttribute("class", "layout-tabmenu-tooltip-arrow", null);
                writer.endElement("div");
                writer.startElement("div", null);
                writer.writeAttribute("class", "layout-tabmenu-tooltip-text", null);
                writer.writeText(tab.getTitle(), null);
                writer.endElement("div");
                writer.endElement("div");
                writer.endElement("a");
                writer.endElement("li");
            }
        }

        writer.endElement("ul");

        writer.startElement("div", tabMenu);
        writer.writeAttribute("class", "layout-tabmenu-contents", null);
        for (int i = 0; i < children.size(); i++) {
            Tab tab = (Tab) children.get(i);

            if (tab.isRendered()) {
                writer.startElement("div", tabMenu);
                writer.writeAttribute("class", "layout-tabmenu-content", null);

                writer.startElement("div", tabMenu);
                writer.writeAttribute("class", "layout-submenu-title clearfix", null);
                writer.startElement("span", tab);
                writer.writeText(tab.getTitle(), null);
                writer.endElement("span");
                writer.endElement("div");

                writer.startElement("div", tabMenu);
                writer.writeAttribute("class", "layout-submenu-content", null);
                writer.startElement("div", null);
                writer.writeAttribute("class", "menu-scroll-content", null);
                tab.encodeAll(context);
                writer.endElement("div");
                writer.endElement("div");

                writer.endElement("div");
            }
        }
        writer.endElement("div");

        writer.endElement("div");
    }

    public void encodeScript(FacesContext context, UIComponent component) throws IOException {
        TabMenu tabMenu = (TabMenu) component;
        String clientId = tabMenu.getClientId(context);
        WidgetBuilder wb = getWidgetBuilder(context);

        wb.init("Morpheus", tabMenu.resolveWidgetVar(), clientId)
                .attr("stateful", tabMenu.isStateful())
                .attr("activeIndex", tabMenu.getActiveIndex());

        encodeClientBehaviors(context, tabMenu);
        wb.finish();
    }

    @Override
    public void encodeChildren(FacesContext context, UIComponent component) throws IOException {
        //Rendering happens on encodeEnd
    }

    @Override
    public boolean getRendersChildren() {
        return true;
    }

    protected void encodeElements(FacesContext context, TabMenu tabMenu, List<MenuElement> elements) throws IOException {
        int size = elements.size();

        for (int i = 0; i < size; i++) {
            encodeElement(context, tabMenu, elements.get(i));
        }
    }

    protected void encodeElement(FacesContext context, TabMenu tabMenu, MenuElement element) throws IOException {
        ResponseWriter writer = context.getResponseWriter();

        if (element.isRendered()) {
            if (element instanceof MenuItem) {
                MenuItem menuItem = (MenuItem) element;
                String menuItemClientId = (menuItem instanceof UIComponent) ? menuItem.getClientId() : menuItem.getClientId();
                String containerStyle = menuItem.getContainerStyle();
                String containerStyleClass = menuItem.getContainerStyleClass();

                writer.startElement("li", null);
                writer.writeAttribute("id", menuItemClientId, null);
                writer.writeAttribute("role", "menuitem", null);

                if (containerStyle != null) {
                    writer.writeAttribute("style", containerStyle, null);
                }
                if (containerStyleClass != null) {
                    writer.writeAttribute("class", containerStyleClass, null);
                }

                encodeMenuItem(context, tabMenu, menuItem);

                writer.endElement("li");
            }
            else if (element instanceof Submenu) {
                Submenu submenu = (Submenu) element;
                String submenuClientId = (submenu instanceof UIComponent) ? ((UIComponent) submenu).getClientId() : submenu.getId();
                String style = submenu.getStyle();
                String styleClass = submenu.getStyleClass();

                writer.startElement("li", null);
                writer.writeAttribute("id", submenuClientId, null);
                writer.writeAttribute("role", "menuitem", null);

                if (style != null) {
                    writer.writeAttribute("style", style, null);
                }
                if (styleClass != null) {
                    writer.writeAttribute("class", styleClass, null);
                }

                encodeSubmenu(context, tabMenu, submenu);

                writer.endElement("li");
            }
            else if (element instanceof Separator) {
                encodeSeparator(context, (Separator) element);
            }
        }
    }

    protected void encodeSubmenu(FacesContext context, TabMenu tabMenu, Submenu submenu) throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        String icon = submenu.getIcon();
        String label = submenu.getLabel();
        int childrenElementsCount = submenu.getElementsCount();

        writer.startElement("a", null);
        writer.writeAttribute("href", "#", null);
        writer.writeAttribute("class", "ripplelink", null);

        encodeItemIcon(context, icon);

        if (label != null) {
            writer.startElement("span", null);
            writer.writeText(label, null);
            writer.endElement("span");

            writer.startElement("span", null);
            writer.writeAttribute("class", "ink animate", null);
            writer.endElement("span");

            encodeToggleIcon(context, submenu, childrenElementsCount);

            if (submenu instanceof UISubmenu) {
                encodeBadge(context, ((UISubmenu) submenu).getAttributes().get("badge"));
            }
        }

        writer.endElement("a");

        //submenus and menuitems
        if (childrenElementsCount > 0) {
            writer.startElement("ul", null);
            writer.writeAttribute("role", "menu", null);
            encodeElements(context, tabMenu, submenu.getElements());
            writer.endElement("ul");
        }
    }

    protected void encodeItemIcon(FacesContext context, String icon) throws IOException {
        if (icon != null) {
            ResponseWriter writer = context.getResponseWriter();

            writer.startElement("i", null);
            writer.writeAttribute("class", "layout-menuitem-icon " + icon, null);
            writer.endElement("i");
        }
    }

    protected void encodeToggleIcon(FacesContext context, Submenu submenu, int childrenElementsCount) throws IOException {
        if (childrenElementsCount > 0) {
            ResponseWriter writer = context.getResponseWriter();

            writer.startElement("i", null);
            writer.writeAttribute("class", "pi pi-fw pi-angle-down layout-menuitem-toggler", null);
            writer.endElement("i");
        }
    }

    protected void encodeBadge(FacesContext context, Object value) throws IOException {
        if (value != null) {
            ResponseWriter writer = context.getResponseWriter();

            writer.startElement("span", null);
            writer.writeAttribute("class", "menuitem-badge", null);
            writer.writeText(value.toString(), null);
            writer.endElement("span");
        }
    }

    protected void encodeSeparator(FacesContext context, Separator separator) throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        String style = separator.getStyle();
        String styleClass = separator.getStyleClass();
        styleClass = styleClass == null ? "Separator" : "Separator " + styleClass;

        //title
        writer.startElement("li", null);
        writer.writeAttribute("class", styleClass, null);
        if (style != null) {
            writer.writeAttribute("style", style, null);
        }

        writer.endElement("li");
    }

    protected void encodeMenuItem(FacesContext context, TabMenu tabMenu, MenuItem menuitem) throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        String title = menuitem.getTitle();
        boolean disabled = menuitem.isDisabled();
        String style = menuitem.getStyle();
        String styleClass = menuitem.getStyleClass();

        writer.startElement("a", null);
        if (title != null) {
            writer.writeAttribute("title", title, null);
        }
        if (style != null) {
            writer.writeAttribute("style", style, null);
        }
        if (styleClass != null) {
            writer.writeAttribute("class", styleClass, null);
        }

        if (disabled) {
            writer.writeAttribute("href", "#", null);
            writer.writeAttribute("onclick", "return false;", null);
        }
        else {
            String onclick = menuitem.getOnclick();

            //GET
            if (menuitem.getUrl() != null || menuitem.getOutcome() != null) {
                String targetURL = getTargetURL(context, (UIOutcomeTarget) menuitem);
                writer.writeAttribute("href", targetURL, null);

                if (menuitem.getTarget() != null) {
                    writer.writeAttribute("target", menuitem.getTarget(), null);
                }
            }
            //POST
            else {
                writer.writeAttribute("href", "#", null);

                UIComponent form = ComponentTraversalUtils.closestForm(context, tabMenu);
                if (form == null) {
                    throw new FacesException("MenuItem must be inside a form element");
                }

                String command;
                if (menuitem.isDynamic()) {
                    String menuClientId = tabMenu.getClientId(context);
                    Map<String, List<String>> params = menuitem.getParams();
                    if (params == null) {
                        params = new LinkedHashMap<>();
                    }
                    List<String> idParams = new ArrayList<>();
                    idParams.add(menuitem.getId());
                    params.put(menuClientId + "_menuid", idParams);

                    command = menuitem.isAjax() ? createAjaxRequest(context, tabMenu, (AjaxSource) menuitem, form, params) : buildNonAjaxRequest(context, tabMenu, form, menuClientId, params, true);

                }
                else {
                    command = menuitem.isAjax() ? createAjaxRequest(context, (AjaxSource) menuitem, form) : buildNonAjaxRequest(context, ((UIComponent) menuitem), form, ((UIComponent) menuitem).getClientId(context), true);
                }
                onclick = (onclick == null) ? command : onclick + ";" + command;
            }

            if (onclick != null) {
                writer.writeAttribute("onclick", onclick, null);
            }
        }

        encodeMenuItemContent(context, menuitem);

        writer.endElement("a");
    }

    protected void encodeMenuItemContent(FacesContext context, MenuItem menuitem) throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        String icon = menuitem.getIcon();
        Object value = menuitem.getValue();

        if (menuitem instanceof UIMenuItem) {
            encodeBadge(context, ((UIMenuItem) menuitem).getAttributes().get("badge"));
        }

        encodeItemIcon(context, icon);

        if (value != null) {
            writer.startElement("span", null);
            writer.writeText(value, "value");
            writer.endElement("span");
        }
    }

    protected String createAjaxRequest(FacesContext context, AjaxSource source, UIComponent form) {
        UIComponent component = (UIComponent) source;
        String clientId = component.getClientId(context);

        AjaxRequestBuilder builder = getAjaxRequestBuilder();

        builder.init()
                .source(clientId)
                .form(SearchExpressionFacade.resolveClientId(context, component, source.getForm()))
                .process(component, source.getProcess())
                .update(component, source.getUpdate())
                .async(source.isAsync())
                .global(source.isGlobal())
                .delay(source.getDelay())
                .timeout(source.getTimeout())
                .partialSubmit(source.isPartialSubmit(), source.isPartialSubmitSet(), source.getPartialSubmitFilter())
                .resetValues(source.isResetValues(), source.isResetValuesSet())
                .ignoreAutoUpdate(source.isIgnoreAutoUpdate())
                .onstart(source.getOnstart())
                .onerror(source.getOnerror())
                .onsuccess(source.getOnsuccess())
                .oncomplete(source.getOncomplete())
                .params(component);

        if (form != null) {
            builder.form(form.getClientId(context));
        }

        builder.preventDefault();

        return builder.build();
    }

    protected String createAjaxRequest(FacesContext context, UIComponent menu, AjaxSource source, UIComponent form,
            Map<String, List<String>> params) {

        String clientId = menu.getClientId(context);

        AjaxRequestBuilder builder = getAjaxRequestBuilder();

        builder.init()
                .source(clientId)
                .process(menu, source.getProcess())
                .update(menu, source.getUpdate())
                .async(source.isAsync())
                .global(source.isGlobal())
                .delay(source.getDelay())
                .timeout(source.getTimeout())
                .partialSubmit(source.isPartialSubmit(), source.isPartialSubmitSet(), source.getPartialSubmitFilter())
                .resetValues(source.isResetValues(), source.isResetValuesSet())
                .ignoreAutoUpdate(source.isIgnoreAutoUpdate())
                .onstart(source.getOnstart())
                .onerror(source.getOnerror())
                .onsuccess(source.getOnsuccess())
                .oncomplete(source.getOncomplete())
                .params(params);

        if (form != null) {
            builder.form(form.getClientId(context));
        }

        builder.preventDefault();

        return builder.build();
    }

    protected AjaxRequestBuilder getAjaxRequestBuilder() {
        Class rootContext;
        Object requestContextInstance;
        AjaxRequestBuilder builder;

        try {
            rootContext = Class.forName("org.primefaces.context.PrimeRequestContext");
        } catch (ClassNotFoundException ex) {
            try {
                rootContext = Class.forName("org.primefaces.context.RequestContext");
            } catch (ClassNotFoundException ex1) {
                throw new RuntimeException(ex1);
            }
        }

        try {
            Method method = rootContext.getMethod("getCurrentInstance");
            requestContextInstance = method.invoke(null);

            method = requestContextInstance.getClass().getMethod("getAjaxRequestBuilder");
            builder = (AjaxRequestBuilder) method.invoke(requestContextInstance);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }

        return builder;
    }
}
Exp;

Code: Select all

// WEB-INF/menu.xhtml
<ui:composition 
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:p="http://primefaces.org/ui"
    xmlns:pm="http://primefaces.org/morpheus">

    <div class="layout-sidebar">
        <h:form>
            <pm:tabMenu id="tabMenu" model="#{menuView.tabs}" />  
        </h:form>
    </div>
</ui:composition>

Code: Select all

// MenuView.java
package org.primefaces.morpheus.view;

import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
import org.primefaces.model.menu.DefaultMenuItem;
import org.primefaces.model.menu.DefaultMenuModel;
import org.primefaces.model.menu.DefaultSubMenu;
import org.primefaces.model.menu.MenuModel;
import org.primefaces.morpheus.component.TabMenuItem;

@Named
@RequestScoped
public class MenuView {

    private List<TabMenuItem> tabs;

    @PostConstruct
    public void init() {
        tabs = new ArrayList<>();

        // Tab 1
        TabMenuItem tab1 = new TabMenuItem("tab1", "pi pi-folder-open", "FEATURES");

        MenuModel model1 = new DefaultMenuModel();
        DefaultSubMenu firstSubmenu = DefaultSubMenu.builder()
                .label("Dynamic Submenu")
                .build();
        DefaultMenuItem item = DefaultMenuItem.builder()
                .value("External")
                .url("http://www.primefaces.org")
                .icon("pi pi-home")
                .build();
        firstSubmenu.getElements().add(item);

        model1.getElements().add(firstSubmenu);
        tab1.setModel(model1);
        tabs.add(tab1);

        // Tab 2
        TabMenuItem tab2 = new TabMenuItem("tab2", "pi pi-inbox", "INBOX");

        MenuModel model2 = new DefaultMenuModel();
        DefaultSubMenu secondSubmenu = DefaultSubMenu.builder()
                .label("Dynamic Actions")
                .build();
        item = DefaultMenuItem.builder()
                .value("Table")
                .icon("pi pi-save")
                .outcome("table")
                .build();
        secondSubmenu.getElements().add(item);

        item = DefaultMenuItem.builder()
                .value("Delete")
                .icon("pi pi-times")
                .ajax(false)
                .build();
        secondSubmenu.getElements().add(item);

        item = DefaultMenuItem.builder()
                .value("Redirect")
                .icon("pi pi-search")
                .build();
        secondSubmenu.getElements().add(item);

        model2.getElements().add(secondSubmenu);
        tab2.setModel(model2);
        tabs.add(tab2);
    }

    public List<TabMenuItem> getTabs() {
        return tabs;
    }
}
Video; https://www.dropbox.com/s/sj1vfx1rg5937 ... t.mov?dl=0

Thanks a lot for your understanding!
Best Regards,