[英]How to implement a recursive menu with PrimeFaces
我正在嘗試創建一個動態菜單:像亞馬遜或eBay上的菜單一樣瀏覽類別。 我的第一次嘗試如下所示:
支持bean:
@ManagedBean
@ViewScoped
public class CategoryBackBean implements ActionListener {
private MenuModel model;
private Category category;
public CategoryBackBean() throws IOException {
category = Category.createRootCategory();
createModel();
}
private void createModel() throws IOException {
MenuModel tempModel = new DefaultMenuModel();
for(Category c : category.getChildCategories()) {
MenuItem childItem = new MenuItem();
childItem.setValue(c.getName());
childItem.addActionListener(this);
tempModel.addMenuItem(childItem);
}
this.model = tempModel;
}
public MenuModel getModel() {
return model;
}
@Override
public void processAction(ActionEvent event) throws AbortProcessingException {
try {
MenuItem item = (MenuItem) event.getSource();
String categoryName = (String) item.getValue();
for(Category c : category.getChildCategories()) {
if(c.getName().equals(categoryName)) {
category = c;
createModel();
return;
}
}
} catch (IOException ex) {
Logger.getLogger(CategoryBackBean.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
網頁:
<h:body>
<h:form>
<p:menubar model="#{categoryBackBean.model}" />
</h:form>
</h:body>
對於初學者,我的設計不起作用:創建了初始菜單,但是當單擊按鈕時,菜單不會在子類別中重新創建。
解決這個普遍問題的最佳方法是什么? 我不是在尋找快速的黑客來讓上面的代碼工作 - 我正在尋找一個遞歸菜單的通用設計。
操作完成后,您需要更新菜單。 鑒於您不想對菜單ID進行硬編碼,請使用@parent
。
childItem.setUpdate("@parent");
如果菜單是表單中的唯一組件,則另一種方法是使用@form
。
這一切是否是“最佳”方式都無法客觀地回答。 如果代碼以最簡單和最少侵入的方式完成您想要的工作,那么它是可以接受的。
解決方案1。
我認為要通知actionListener,首先必須有一個動作事件。 所以我嘗試添加這個丑陋的代碼(肯定有更好的方法嗎?)為每個childItem添加一個動作事件:
private void createModel() throws IOException {
FacesContext facesCtx = FacesContext.getCurrentInstance();
ELContext elCtx = facesCtx.getELContext();
ExpressionFactory expFact = facesCtx.getApplication().getExpressionFactory();
MenuModel tempModel = new DefaultMenuModel();
for(Category c : childCategories) {
MenuItem childItem = new MenuItem();
childItem.setValue(c.getName());
childItem.addActionListener(this);
childItem.setActionExpression(expFact.createMethodExpression(elCtx, "#{categoryBackBean.doSomething()}", void.class, new Class[0]));
tempModel.addMenuItem(childItem);
}
this.model = tempModel;
}
doSomething()
只是一個虛方法,以便調用動作監聽器。
這導致了在提交響應之后創建會話的問題 ,這需要這個hack:
@PostConstruct
public void sessionStart() {
FacesContext.getCurrentInstance().getExternalContext().getSession(true);
}
畢竟,我需要在每個元素上禁用Ajax,以便提交表單:
childItem.setAjax(false);
總而言之,一個黑客鏈,但它現在起作用(沒有ajax)。
另一個解決方案,解決方案2.此解決方案避免了設置動作表達式的需要,但是,它確實引入了對XHTML代碼的向后依賴性(在這種情況下, <p:menubar ..>
必須具有“menuid”的id)。
private void createModel() throws IOException {
MenuModel tempModel = new DefaultMenuModel();
for(Category c : childCategories) {
MenuItem childItem = new MenuItem();
childItem.setValue(c.getName());
childItem.addActionListener(this);
childItem.setUpdate("menuId"); // Magic new line.
tempModel.addMenuItem(childItem);
}
this.model = tempModel;
}
這可以通過使每個菜單項通過Ajax更新整個菜單欄來實現。 如果有一些方法可以獲得菜單的ID沒有硬編碼...
解決方案3.此解決方案非常簡單。 支持bean:
@ManagedBean
@ViewScoped
public class CategoryBackBean implements Serializable {
private Category category = Category.createRootCategory();
public void setCategory(Category category) {
this.category = category;
}
public Category getCategory() {
return category;
}
}
網頁:
<h:form>
<p:menu id="myMenu">
<c:forEach items="#{categoryBackBean.category.childCategories}" var="subCategory">
<p:menuitem value="#{subCategory.name}" update="myMenu">
<f:setPropertyActionListener target="#{categoryBackBean.category}" value="#{subCategory}" />
</p:menuitem>
</c:forEach>
</p:menu>
</h:form>
值得注意的要點:
<c:forEach>
代替<ui:repeat>
因為后者在Primefaces菜單中不起作用。
update="myMenu"
在XHTML中設置,而不在支持代碼中。 這解決了我的解決方案#2的問題。
<ui:repeate>
或<c:forEach>
創建菜單(我會反對這一點,給出技術的簡單性<c:forEach>
)。 更新
不要用這個答案! <c:forEach/>
'在創建組件樹之前運行',並且在頁面更新時不會通過Ajax或使用回發重新運行。 它對我有用的唯一原因是由於第一類具有最多的子類別,因此,它最初將為任何其他類別創建具有足夠<p:menuitems />
的組件樹。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.