ContextMenu can have nested submenus and menuitems
<div class="card">
Right click to view the menu options.
<h:form>
<p:growl id="messages" showDetail="true"/>
<p:contextMenu style="width:200px">
<p:submenu label="Ajax Menuitems" icon="pi pi-refresh">
<p:menuitem value="Save" action="#{menuView.save}" update="messages" icon="pi pi-save"/>
<p:menuitem value="Update" action="#{menuView.update}" update="messages" icon="pi pi-refresh"/>
</p:submenu>
<p:submenu label="Non-Ajax Menuitem" icon="pi pi-clone">
<p:menuitem value="Delete" action="#{menuView.delete}" update="messages" ajax="false"
icon="pi pi-times"/>
</p:submenu>
<p:divider />
<p:submenu label="Navigations" icon="pi pi-home">
<p:submenu label="External">
<p:menuitem value="PrimeTek" url="https://www.primetek.com.tr"/>
<p:menuitem value="PrimeFaces" url="https://www.primefaces.org"/>
</p:submenu>
<p:menuitem value="Mobile" icon="pi pi-mobile"/>
</p:submenu>
</p:contextMenu>
</h:form>
</div>
package org.primefaces.showcase.view.menu;
import jakarta.annotation.PostConstruct;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.context.ExternalContext;
import jakarta.faces.context.FacesContext;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Named;
import java.io.IOException;
import java.io.Serial;
import java.io.Serializable;
import java.util.concurrent.TimeUnit;
import io.quarkus.runtime.annotations.RegisterForReflection;
import org.primefaces.model.menu.DefaultMenuItem;
import org.primefaces.model.menu.DefaultMenuModel;
import org.primefaces.model.menu.DefaultSubMenu;
import org.primefaces.model.menu.MenuModel;
@Named
@ViewScoped
@RegisterForReflection(serialization = true)
public class MenuView implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private MenuModel model;
@PostConstruct
public void init() {
model = new DefaultMenuModel();
//First submenu
DefaultSubMenu firstSubmenu = DefaultSubMenu.builder()
.label("Options")
.expanded(true)
.build();
DefaultMenuItem item = DefaultMenuItem.builder()
.value("Save (Non-Ajax)")
.icon("pi pi-save")
.ajax(false)
.command("#{menuView.save}")
.update("messages")
.build();
firstSubmenu.getElements().add(item);
item = DefaultMenuItem.builder()
.value("Update")
.icon("pi pi-refresh")
.command("#{menuView.update}")
.update("messages")
.build();
firstSubmenu.getElements().add(item);
item = DefaultMenuItem.builder()
.value("Delete")
.icon("pi pi-times")
.command("#{menuView.delete}")
.update("messages")
.build();
firstSubmenu.getElements().add(item);
model.getElements().add(firstSubmenu);
//Second submenu
DefaultSubMenu secondSubmenu = DefaultSubMenu.builder()
.label("Navigations")
.expanded(false)
.build();
item = DefaultMenuItem.builder()
.value("Website")
.url("http://www.primefaces.org")
.icon("pi pi-external-link")
.build();
secondSubmenu.getElements().add(item);
item = DefaultMenuItem.builder()
.value("Internal")
.icon("pi pi-upload")
.command("#{menuView.redirect}")
.build();
secondSubmenu.getElements().add(item);
model.getElements().add(secondSubmenu);
}
public MenuModel getModel() {
return model;
}
public void redirect() throws IOException {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.redirect(ec.getRequestContextPath());
}
public void save() {
addMessage("Save", "Data saved");
}
public void update() {
addMessage("Update", "Data updated");
}
public void delete() {
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_WARN, "Delete", "Data deleted");
FacesContext.getCurrentInstance().addMessage(null, message);
}
public void sleepAndSave() throws InterruptedException {
TimeUnit.SECONDS.sleep(1);
save();
}
public void sleepAndUpdate() throws InterruptedException {
TimeUnit.SECONDS.sleep(1);
update();
}
public void sleepAndDelete() throws InterruptedException {
TimeUnit.SECONDS.sleep(1);
delete();
}
public void addMessage(String summary, String detail) {
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, summary, detail);
FacesContext.getCurrentInstance().addMessage(null, message);
}
}