3

私は動的メニューを作成しようとしています。Amazonや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>

手始めに、私のデザインは機能しません。最初のメニューは作成されますが、ボタンをクリックしても、メニューはサブカテゴリに再作成されません。

この一般的な問題に取り組むための最良の方法は何ですか?上記のコードを機能させるための簡単なハックは探していません。再帰メニューの一般的なデザインを探しています。

4

4 に答える 4

5

アクションが完了した後、メニューを更新する必要があります。メニューIDをハードコーディングしたくない場合は、を使用します@parent

childItem.setUpdate("@parent");

別の方法として、メニューがフォームの唯一のコンポーネントである場合は、を使用するだけ@formです。

これがすべて「最善の」方法であるかどうかは、客観的に答えることはできません。コードが可能な限り単純で邪魔にならない方法で必要な仕事を正確に実行する場合、それは許容範囲です。

于 2013-01-12T01:03:57.347 に答える
0

別の解決策、解決策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を取得する方法があった場合...

于 2012-11-30T04:08:48.880 に答える
0

解決策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()アクションリスナーを呼び出すためのダミーメソッドはどこにありますか。

これにより、コミットされた応答の後にセッションを作成する際にこの問題が発生し、このハックが必要になりました。

@PostConstruct
public void sessionStart() {
    FacesContext.getCurrentInstance().getExternalContext().getSession(true);
}

その後、フォームが送信されるように、各要素でAjaxを無効にする必要がありました。

childItem.setAjax(false);

要約すると、ハックチェーンですが、現在は機能しています(ajaxなし)。

于 2012-11-29T22:31:12.050 に答える
0

解決策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;
  }
}

Webページ:

<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>)。

アップデート

この答えを使用しないでください!'runs'は<c:forEach/>コンポーネントツリーが作成される前に実行され、Ajaxまたはポストバックを使用してページが更新されたときに再実行されません。それが私のために機能した唯一の理由は、最初のカテゴリが最も多くのサブカテゴリを持っているためであり、したがって、最初は<p:menuitems />他のカテゴリに十分なコンポーネントツリーを作成していました。

于 2012-12-01T00:44:32.573 に答える