Проверка нескольких входных данных в ap: dataTable или p: treeTable с использованием OmniFaces o: validateMultiple - PullRequest
1 голос
/ 15 февраля 2020

У меня есть пользовательский интерфейс, который выглядит следующим образом:

enter image description here

В игре не должно быть одинаковых номеров джерси, поэтому я хочу валидатор для все 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 ...

enter image description here

.. ... идентификаторы, которые я использую, кажутся правильными, и политика поиска parent.findComponent(clientId) может не работать для таких итеративных компонентов, как p:dataTable и p:treeTable ... ?‍♂️

Я был бы рад если кто-то может взглянуть на это.

Может быть, есть другое решение для этого, но я просто не вижу его, поэтому, если есть другой способ проверки нескольких входных данных в p:dataTable или p:treeTable, пожалуйста, дайте мне знать.


РЕДАКТИРОВАТЬ:

Я использовал p:dataTable, чтобы объяснить мою проблему, однако я не знал, что этот компонент имеет совершенно другую иерархию наследования. p:dataTable расширяется от UIData, а p:treeTable расширяется от UITree.

Очевидно, что иерархические данные, конечно, более сложны.

Вот реальный пользовательский интерфейс, который я использую:

enter image description here

Это приводит к исключению при использовании 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)

1 Ответ

2 голосов
/ 15 февраля 2020

<o:validateMultiple> (пока?) Не предназначен для использования таким образом, нацеливаясь на один компонент внутри компонента повторителя. Он работает только с физически несколькими компонентами, определенными в исходном коде X HTML, а не с фактически несколькими компонентами, созданными в выводе HTML.

Пока просто используйте <o:validateUniqueColumn>. Он предназначен именно для вашего случая использования. Это тег-обработчик, а не компонент, поэтому просто вложите его непосредственно в один компонент ввода, как показано ниже. Нет необходимости в другой конфигурации.

<p:column>
    <p:inputNumber>
        <o:validateUniqueColumn />
    </p:inputNumber>
</p:column>
...