У меня есть пользовательский интерфейс, который выглядит следующим образом:
В игре не должно быть одинаковых номеров джерси, поэтому я хочу валидатор для все UIInputs в этом столбце.
Я сразу придумал использовать 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?
Просмотр Код OmniFaces в ValidateMultipleFields
классе
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 ...
.. ... идентификаторы, которые я использую, кажутся правильными, и политика поиска 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)