我有一个看起来像这样的UI:
游戏中不能有重复的球衣号码,所以我想要该列中所有UIInput的验证器。
我立即想到使用OmniFaces的o:validateMultiple
。
但是,这似乎不起作用。通过单击按钮提交表单将导致:
22:30:20,917 SEVERE [javax.enterprise.resource.webcontainer.jsf.context] (default task-7) java.lang.IllegalArgumentException: ValidateMultiple attribute 'components' must refer existing client IDs. Client ID ':score-input-form:home-box-score-table:0:jersey-nbr-input' cannot be found.
at org.omnifaces.component.validator.ValidateMultipleFields.findInputComponent(ValidateMultipleFields.java:324)
at org.omnifaces.component.validator.ValidateMultipleFields.collectComponents(ValidateMultipleFields.java:248)
at org.omnifaces.component.validator.ValidateMultipleFields.validateComponents(ValidateMultipleFields.java:205)
at org.omnifaces.component.validator.ValidatorFamily.processValidators(ValidatorFamily.java:68)
at javax.faces.component.UIForm.processValidators(UIForm.java:269)
at com.sun.faces.context.PartialViewContextImpl$PhaseAwareVisitCallback.visit(PartialViewContextImpl.java:632)
at com.sun.faces.component.visit.PartialVisitContext.invokeVisitCallback(PartialVisitContext.java:183)
at javax.faces.component.UIForm.visitTree(UIForm.java:405)
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1747)
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1747)
at com.sun.faces.context.PartialViewContextImpl.processComponents(PartialViewContextImpl.java:424)
at com.sun.faces.context.PartialViewContextImpl.processPartial(PartialViewContextImpl.java:285)
at org.primefaces.context.PrimePartialViewContext.processPartial(PrimePartialViewContext.java:65)
at javax.faces.context.PartialViewContextWrapper.processPartial(PartialViewContextWrapper.java:252)
at org.omnifaces.context.OmniPartialViewContext.processPartial(OmniPartialViewContext.java:122)
at javax.faces.component.UIViewRoot.processValidators(UIViewRoot.java:1330)
at com.sun.faces.lifecycle.ProcessValidationsPhase.execute(ProcessValidationsPhase.java:77)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:100)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:201)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:670)
at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129)
at io.opentracing.contrib.jaxrs2.server.SpanFinishingFilter.doFilter(SpanFinishingFilter.java:55)
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)
at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68)
at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
at org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:132)
at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)
at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)
at io.undertow.security.handlers.NotificationReceiverHandler.handleRequest(NotificationReceiverHandler.java:50)
at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at org.wildfly.extension.undertow.deployment.GlobalRequestControllerHandler.handleRequest(GlobalRequestControllerHandler.java:68)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:292)
at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:81)
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:138)
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135)
at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at org.wildfly.extension.undertow.security.SecurityContextThreadSetupAction.lambda$create$0(SecurityContextThreadSetupAction.java:105)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272)
at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81)
at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:104)
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:360)
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:830)
at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1985)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1487)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1378)
at java.lang.Thread.run(Thread.java:748)
这是我的代码示例...
Bean:
@Named
@ViewScoped
public class DebugTableValidateMultipleManager implements Serializable
{
private static final long serialVersionUID = 1L;
private SimpleScore selectedEntity;
private TreeNode rootNode;
@PostConstruct
public void init()
{
this.selectedEntity = new SimpleScore();
List<SimplePlayerStat> playerStats = Arrays.asList( new SimplePlayerStat( "Michael Jordan", 23 ), new SimplePlayerStat( "Dirk Nowitzki", 41 ) );
this.selectedEntity.setPlayerStats( playerStats );
}
public SimpleScore getSelectedEntity()
{
return selectedEntity;
}
public void setSelectedEntity( SimpleScore selectedEntity )
{
this.selectedEntity = selectedEntity;
}
public void save()
{
System.out.println( "Saving simple score: " + selectedEntity );
FacesContext.getCurrentInstance().addMessage( "score-button-form:growl", new FacesMessage( "Score successfully saved." ) );
}
public TreeNode getRootNode()
{
// System.out.println( "------- getRootNode(): " + rootNode );
if ( rootNode == null )
{
System.out.println( getClass().getSimpleName() + ": -------------------------------------- Selected score: " + selectedEntity );
// init navigation tree
rootNode = new DefaultTreeNode( selectedEntity, null );
List<SimplePlayerStat> playerStats = selectedEntity.getPlayerStats();
for ( SimplePlayerStat playerStat : playerStats )
{
// player stats: jersey nbr, starter, DNP
TreeNode playerStatNode = new DefaultTreeNode( SimplePlayerStat.class.getSimpleName(), playerStat, rootNode );
playerStatNode.setExpanded( true );
}
}
return rootNode;
}
public void setRootNode( TreeNode rootNode )
{
this.rootNode = rootNode;
}
// validation
public void validateJerseyNbrs( FacesContext context, List<UIInput> components, List<Object> values )
{
// get home or away score entity
SimpleScore score = selectedEntity;
List<Integer> jerseyNbrs = score.getPlayerStats().stream().map( SimplePlayerStat::getJerseyNbr ).collect( Collectors.toList() );
// determine frequencies of jersey nbrs
Map<Integer, Long> frequencies = jerseyNbrs.stream().collect( Collectors.groupingBy( j -> j, Collectors.counting() ) );
System.out.println( "Frequencies:\n" + frequencies );
// if ( players.contains( player ) )
{
throw new ValidatorException( FacesMsgUtils.newErrorMessage( "There are " + frequencies + " of jersey numbers in this!" ) );
}
}
public String getValidatorIds()
{
IntStream indices = IntStream.range( 0, selectedEntity.getPlayerStats().size() );
List<String> ids = indices.mapToObj( id -> ":score-input-form:home-box-score-table:" + id + ":jersey-nbr-input" ).collect( Collectors.toList() );
String idString = ids.stream().collect( Collectors.joining( " " ) );
System.out.println( "Validator IDs: " + idString );
return idString;
}
public class SimpleScore implements Serializable
{
private static final long serialVersionUID = 1L;
private List<SimplePlayerStat> playerStats;
public List<SimplePlayerStat> getPlayerStats()
{
return playerStats;
}
public void setPlayerStats( List<SimplePlayerStat> playerStats )
{
this.playerStats = playerStats;
}
}
public class SimplePlayerStat implements Serializable
{
private static final long serialVersionUID = 1L;
private String name;
private Integer jerseyNbr;
public SimplePlayerStat( String name, Integer jerseyNbr )
{
this.name = name;
this.jerseyNbr = jerseyNbr;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public Integer getJerseyNbr()
{
return jerseyNbr;
}
public void setJerseyNbr(Integer jerseyNbr)
{
this.jerseyNbr = jerseyNbr;
}
}
}
Facelet:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:o="http://omnifaces.org/ui"
xmlns:of="http://omnifaces.org/functions"
xmlns:p="http://primefaces.org/ui">
<f:view>
<h:head>
</h:head>
<h:body>
<div style="margin-left: auto; margin-right: auto; width: 500px; font-size: 0.8em;">
<h1>Game Manager</h1>
<h:form id="score-input-form">
<!--p:treeTable id="home-box-score-table"
value="#{debugTableValidateMultipleManager.rootNode}"
var="obj"
nodeVar="node"
styleClass="box-score-tree-table">
<f:facet name="header">
Home Team
</f:facet>
<p:column id="name"
headerText="Player"
styleClass="width-30 text-left">
<h:outputText value="#{obj.name}" />
</p:column>
<p:column id="jersey-nbr"
headerText="Jersey Number"
styleClass="width-10 text-right">
<p:inputNumber id="jersey-nbr-input"
value="#{obj.jerseyNbr}"
required="true"
requiredMessage="Jersey number is required."
maxlength="2"
decimalPlaces="0"
styleClass="" />
</p:column>
</p:treeTable-->
<p:dataTable id="home-box-score-table"
value="#{debugTableValidateMultipleManager.rootNode.data.playerStats}"
var="obj"
styleClass="box-score-tree-table">
<f:facet name="header">
Home Team
</f:facet>
<p:column id="name"
headerText="Player"
styleClass="width-30 text-left">
<h:outputText value="#{obj.name}" />
</p:column>
<p:column id="jersey-nbr"
headerText="Jersey Number"
styleClass="width-10 text-right">
<p:inputNumber id="jersey-nbr-input"
value="#{obj.jerseyNbr}"
required="true"
requiredMessage="Jersey number is required."
maxlength="2"
decimalPlaces="0"
styleClass="" />
</p:column>
</p:dataTable>
<div class="clear-both" />
<o:validateMultiple id="home-validator"
components=":score-input-form:home-box-score-table:0:jersey-nbr-input :score-input-form:home-box-score-table:1:jersey-nbr-input"
omponents="@form:home-box-score-table:0:jersey-nbr-input"
mponents="#{debugTableValidateMultipleManager.validatorIds}"
validator="#{debugTableValidateMultipleManager.validateJerseyNbrs}" />
<p:messages severity="error" />
<p:commandButton id="save-button"
icon="fa fa-save"
value="Save"
action="#{debugTableValidateMultipleManager.save()}"
process="@form"
update="@form"
style="margin-top: 10px;" />
</h:form>
</div>
</h:body>
</f:view>
</html>
问题:
为什么不起作用?
这是OmniFaces中的错误/缺失功能吗?
查看ValidateMultipleFields
类中的OmniFaces代码
private UIInput findInputComponent(UIComponent parent, String clientId, PropertyKeys property) {
UIComponent found = parent.findComponent(clientId); <-- results in null!
if (found == null) {
throw new IllegalArgumentException(format(
ERROR_UNKNOWN_COMPONENT, getClass().getSimpleName(), property, clientId));
}
else if (!(found instanceof UIInput)) {
throw new IllegalArgumentException(format(
ERROR_INVALID_COMPONENT, getClass().getSimpleName(), property, clientId, found.getClass().getName()));
}
return (UIInput) found;
}
并查看生成的HTML ...
...我正在使用的ID似乎是正确的,并且搜索策略parent.findComponent(clientId)
可能不适用于p:dataTable
和p:treeTable
...🤷迭代♂等迭代组件>
如果有人可以看一下,我会很高兴。
也许对此有另一种解决方案,但我简单看不到,所以如果有另一种方法可以验证p:dataTable
或p:treeTable
中的多个输入,请告诉我。
编辑:
我用p:dataTable
来解释我的问题,但是我不知道该组件具有完全不同的继承层次结构。 p:dataTable
从UIData
延伸,而p:treeTable
从UITree
延伸。
显然,分层数据当然更复杂。
这是我正在使用的真实用户界面:
这会导致使用o:validateUniqueColumn
的异常:
00:02:17,223 SEVERE [javax.enterprise.resource.webcontainer.jsf.context] (default task-2) java.lang.IllegalArgumentException: Parent component of o:validateUniqueColumn must be enclosed in an UIData component.
at org.omnifaces.taghandler.ValidateUniqueColumn.apply(ValidateUniqueColumn.java:114)
at javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:161)
at com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:203)
at javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:135)
at javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:96)
at javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:161)
at com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:203)
at javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:135)
at javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:96)
at javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:161)
at com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:203)
at javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:135)
at javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:96)
at com.sun.faces.facelets.tag.ui.CompositionHandler.apply(CompositionHandler.java:195)
at com.sun.faces.facelets.compiler.NamespaceHandler.apply(NamespaceHandler.java:94)
at com.sun.faces.facelets.compiler.EncodingHandler.apply(EncodingHandler.java:88)
我有一个如下所示的用户界面:游戏中不得有重复的球衣号码,因此我需要该列中所有UIInput的验证器。我立即想出了使用OmniFaces的o:...
<o:validateMultiple>
还不是要以这种方式使用,而是以中继器组件中的单个组件为目标。它仅对XHTML源代码中定义的物理多个组件起作用,而实际上不适用于HTML输出中产生的多个组件。