PrimeFaces动态菜单:无法删除相同的组件两次和ID生成

时间:2016-06-28 16:00:05

标签: jsf jsf-2 primefaces

我在<p:menu>中有一个<p:dataTable>,在升级到Mojarra 2.1.22(在GlassFish 3.1.2.2上)时,我们会得到不定期FacesException

        <p:dataTable id="data"
                     widgetVar="resultDataTable" 
                     value="#{depotManager.dataModel}"
                     var="dep"
                     rowKey="#{dep.id}"
                     selection="#{depotManager.selectedEntities}"
                     selectionMode="#{depotManager.singleSelectionMode ? 'single' : 'multiple'}"
                     ...
                     emptyMessage="#{msg['entity.depot.list.emptyMessage']}">

            <p:ajax event="rowSelect"...

            <f:facet name="header">
                <h:panelGroup id="header"
                              layout="block">

                    <p:menu overlay="true"
                            trigger="sub-client-button"
                            my="left top"
                            at="left bottom"
                            model="#{accountManagerMenuKeeper.subClientMenuModel}" />

我们得到的例外是:

javax.faces.FacesException: Cannot remove the same component twice: content-form:data:menu-item-1

类似的问题在这里:http://forum.primefaces.org/viewtopic.php?f=3&t=23669

我做了一些调查,问题似乎是我们正在使用PrimeFaces 3.5.28的视图范围的bean。我将菜单bean切换到请求范围,如下所述:

http://blog.primefaces.org/?p=2594

我们无法切换到PrimeFaces 4 ......一些例外情况消失了,但不是全部(可能是其中的一半)。所以问题仍然存在。

现在使用唯一ID生成更好地理解问题,阅读https://java.net/jira/browse/JAVASERVERFACES-2283,我可能需要在dataTable命名容器下生成ID。

dataTable的ID是“content-form:data”,所以我想我需要为每个动态菜单项生成唯一的ID,我当前没有这样做(每个客户端一个菜单项,附加简单计数器) ):

        for ( Client subClient : subClients )
        {
            MenuItem item = new MenuItem();
            item.setId( "menu-item-" + ( ++i ) );
//            item.setId( "content-form:data:menu-item-" + ( ++i ) );
            ...
        }

问:

如何在此处生成正确的ID,例如使用JSF的内置方法,如createUniqueId()等。

对我来说这是一个小问题,因为例外提到了前面有dataTable ID的完整ID:javax.faces.FacesException: Cannot remove the same component twice: content-form:data:menu-item-1 ...

1 个答案:

答案 0 :(得分:0)

即使在使用简单的请求范围的菜单创建bean的PrimeFaces 3.5.28上,问题也消失了:

@Named
@RequestScoped
public class AccountManagerMenuKeeper implements Serializable
{
    private static final long serialVersionUID = 1L;

    @Inject
    @SeamLogger
    protected Logger          log;

    @Inject
    protected SessionHelper   sessionHelper;

    @Inject
    private DepotManager      accountManager;

    private MenuModel         subClientMenuModel;

    /**
     * @return the subClientMenuModel
     */
    public MenuModel getSubClientMenuModel()
    {
        if ( this.subClientMenuModel == null )
        {
            buildSubClientMenuModel();
        }

        return ( this.subClientMenuModel );
    }

    /**
     * Builds the menu model for adding sub-clients.
     * 
     */
    private void buildSubClientMenuModel()
    {
        this.subClientMenuModel = new DefaultMenuModel();
        List<Client> subClients = this.accountManager.getWritableSubClients();

        if ( subClients == null || subClients.isEmpty() )
            return;

        // Necessary instances
        FacesContext facesContext = FacesContext.getCurrentInstance();
        ELContext elContext = facesContext.getELContext();
        Application application = facesContext.getApplication();
        ExpressionFactory factory = application.getExpressionFactory();

        int i = 0;

        for ( Client subClient : subClients )
        {
            MenuItem item = new MenuItem();
            item.setId( "menu-item-" + ( ++i ) );

            item.setValue( subClient.getName() );
            item.setProcess( "@this" );
            item.setUpdate( ":content-form" );
            item.setDisabled( this.accountManager.isReadOnly() || !this.accountManager.isSingleSelectionMode() || EMode.ADD.equals( this.accountManager.getMode() ) || EMode.EDIT.equals( this.accountManager.getMode() ) );
            item.setActionExpression( factory.createMethodExpression( elContext, "#{depotManager.add}", String.class, new Class[] {} ) );

            ValueExpression targetExpression = factory.createValueExpression( elContext, "#{depotManager.selectedSubClientId}", Long.class );
            ValueExpression valueExpression = factory.createValueExpression( elContext, "" + subClient.getId(), Long.class );
            item.addActionListener( new SwitchableSetPropertyActionListener( targetExpression, valueExpression ) );

            this.subClientMenuModel.addMenuItem( item );
        }
    }
}

这将/应该适用于较新的Mojarra版本。 (DUH)