我在<p:dataTable>
中有一个<p:menu>
,当升级到Mojarra 2.1.22(在GlassFish 3.1.2.2上)时,我们得到了不规则的FacesException
s:
<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()等?
这对我来说有点奇怪,因为例外提到了完整的ID与dataTable的ID在前面:javax.faces.FacesException: Cannot remove the same component twice: content-form:data:menu-item-1
…
使用简单的请求作用域的菜单创建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版本一起工作。(咄)