簡體   English   中英

如何使用PrimeFaces實現遞歸菜單

[英]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的問題。

  • 設計與建議的Primefaces設計啟發式相反,正如這里所暗示的那樣,應該使用支持模型而不是使用<ui:repeate><c:forEach>創建菜單(我會反對這一點,給出技術的簡單性<c:forEach> )。

更新

不要用這個答案! <c:forEach/> '在創建組件樹之前運行',並且在頁面更新時不會通過Ajax或使用回發重新運行。 它對我有用的唯一原因是由於第一類具有最多的子類別,因此,它最初將為任何其他類別創建具有足夠<p:menuitems />的組件樹。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM