3

I have a form with some required fields depending on a selectOneMenu, like the following:

<p:selectOneMenu id="myList" value="#{myBean.selectedItem}">
    <p:selectItems value="#{myBean.myItems}" />
    <p:ajax listener="#{myBean.myList_change}"
            process="myList field1 field2 field3"
            update="field1 field2 field3" />
</p:selectOneMenu>

<p:inputNumber id="field1" required="true" />
<p:inputNumber id="field2" required="#{myBean.selectedItem gt 1}" />
<p:inputNumber id="field3" required="#{myBean.selectedItem gt 2}" />

The first time I push the submit button, without fill the required fields:

<p:commandButton id="mySubmit" action="#{myBean.myAction}" />

I get the validation errors, then if I change the selectOneMenu value the required expression will no longer be evaluated.

For example, if I submit the form with selectedItem equals to 3 I get all the validation errors, then I submit the form with selectedItem equals to 1 and primefaces still requires all the three fields as mandatory.

I tried to add a resetInput to the button:

<p:resetInput target=":myForm" />

Rather than the immediate="true" to the selectOneMenu, without any success.

Any idea, please?

Note: The PrimeFaces version is 6.2

PS: In the image, just the first field is required, but should required all the elements.

example

Following the complete minimal reproducible example:

The xhtml file (test.xhtml):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:p="http://primefaces.org/ui">
<h:head>
    <title>Example</title>
</h:head>
<h:body bgcolor="white">
    <h:form>
        <p:messages showDetail="true" />
        <p:selectOneMenu id="myList" value="#{myBean.selectedItem}">
            <f:selectItem itemLabel="1" itemValue="1" />
            <f:selectItem itemLabel="2" itemValue="2" />
            <f:selectItem itemLabel="3" itemValue="3" />
            <p:ajax listener="#{myBean.myList_change}"
                process="myList field1 field2 field3" update="field1 field2 field3" />
        </p:selectOneMenu>

        <p:inputNumber id="field1" required="true" decimalPlaces="0"
            value="#{myBean.field1}" />
        <p:inputNumber id="field2" required="#{myBean.selectedItem gt 1}"
            decimalPlaces="0" value="#{myBean.field2}" />
        <p:inputNumber id="field3" required="#{myBean.selectedItem gt 2}"
            decimalPlaces="0" value="#{myBean.field3}" />

        <p:commandButton actionListener="#{myBean.submit}" update="@form" />
    </h:form>
</h:body>
</html>

The bean (MyBean.java):

    package com.mkyong.common;

import java.io.Serializable;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

@ManagedBean(name = "myBean")
@SessionScoped
public class MyBean implements Serializable {

    private static final long serialVersionUID = 1L;
    private static final String STRING_EMPTY = "";

    private Long selectedItem;

    private String field1, field2, field3;

    public Long getSelectedItem() {
        return selectedItem;
    }

    public void setSelectedItem(final Long selectedItem) {
        this.selectedItem = selectedItem;
    }

    public String getField1() {
        return field1;
    }

    public void setField1(final String field1) {
        this.field1 = field1;
    }

    public String getField2() {
        return field2;
    }

    public void setField2(final String field2) {
        this.field2 = field2;
    }

    public String getField3() {
        return field3;
    }

    public void setField3(final String field3) {
        this.field3 = field3;
    }

    public void myList_change() {
        final long value = selectedItem.longValue();
        if (value < 1) {
            setField1(STRING_EMPTY);
        }
        if (value < 2) {
            setField2(STRING_EMPTY);
        }
        if (value < 3) {
            setField3(STRING_EMPTY);
        }
    }

    public void submit() {
    }

}

In the web.xml you should set the welcome-page:

<welcome-file-list>
    <welcome-file>faces/test.xhtml</welcome-file>
</welcome-file-list>
Kukeltje
  • 12,223
  • 4
  • 24
  • 47
Alessandro
  • 4,382
  • 8
  • 36
  • 70
  • Can you add your PF version to the question? – Kukeltje Jul 08 '19 at 13:09
  • 1
    Why do you process all the fields on change of the selectOneMenu? Try `process="@this"` instead of `process="myList field1 field2 field3"` – Selaron Jul 08 '19 at 13:12
  • @Kukeltje I've added the version. Thanks. – Alessandro Jul 08 '19 at 13:13
  • @Selaron because I've to clear the old value when I reduce the number of fields. – Alessandro Jul 08 '19 at 13:14
  • This can be done by your ajax listener `#{myBean.myList_change}` or an additional valueChangeListener. The process attribute won't clear anything. – Selaron Jul 08 '19 at 13:22
  • Also check if this works with plain jsf counterparts. If it does not, you can sort of be sure it is a pure jsf thing and not a PrimeFaces related one. – Kukeltje Jul 08 '19 at 13:28
  • @Selaron Yes, I do it in the ajax listener `#{myBean.myList_change}` and I specify the process attribute to avoid to pass the entire form. – Alessandro Jul 08 '19 at 13:28
  • Can you make a [mcve] with what you actually do? – Kukeltje Jul 08 '19 at 13:40
  • @Kukeltje I've tried with the JSF tags and it works fine. Can't you reproduce my example in the question? – Alessandro Jul 08 '19 at 13:47
  • So if you create a new, empty project and copy **JUST** the code above in there, you can reproduce? If you can, I could try to reproduce as well, but I'm 100% sure you can't. Hence I can't either. – Kukeltje Jul 08 '19 at 14:00
  • @Kukeltje I've added the complete minimal reproducible example. Thank you. – Alessandro Jul 08 '19 at 15:17
  • Your inputNumber fields are not bound to any getters that you do have in the java code. Any specific reason for this? You get a sort of undefined behaviour then. Since the components do not really have something to check against... – Kukeltje Jul 08 '19 at 16:13
  • And selectedItem is a STRING... doing a 'gt' on a string and a number is weird. And the field1, 2 and 3 are strings too and while not bound to a inputNumber via `value=`, the design is already strange... Please create a good logical functional piece of code first! – Kukeltje Jul 08 '19 at 16:20
  • And please read https://stackoverflow.com/questions/3909267/differences-between-action-and-actionlistener#3909382 – Kukeltje Jul 08 '19 at 17:31
  • @Kukeltje I've updated my question changing the type of `selectedItem` and bounding the inputNumber fields, but I've got the same error. What do you mean about the `actionListener`? Thanks. – Alessandro Jul 09 '19 at 06:26
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/196175/discussion-between-kukeltje-and-alessandro). – Kukeltje Jul 09 '19 at 06:36
  • 1
    OT: iupvotedbecau.se nice and constructive interaction between you guys. Huge contrast to other OPs never giving feedback on comments. – Selaron Jul 09 '19 at 09:55

1 Answers1

5

This question initially was sort of strange, weird since it contained some illogical behaviour that could, like OP stated, not be solved with a p:resetInput or a resetValues="true" attribute on the p:ajax.

Compliments to @Alessandro for being responsive on the comments by initially creating a [mcve] and later on in the chat. Thanks for that, rewarding in comparison to some other people that fail to see the value of a [mcve] and being active in replying.

The first thing I did was change some illogical things in the code that might not be related

  • The type in the backingbean for selectedItem from a String to an int since that is what it effectively was.
  • Change the fields from String to int's too since they are with a p:inputNumber
  • Set the fields to null instead of empty String on a change of the select.

Java code:

import java.io.Serializable;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

@ManagedBean(name = "myBean")
@SessionScoped
public class MyBean implements Serializable {

    private static final long serialVersionUID = 1L;

    private int selectedItem;

    private Integer field1, field2, field3;

    public int getSelectedItem() {
        return selectedItem;
    }

    public void setSelectedItem(final int selectedItem) {
        this.selectedItem = selectedItem;
    }

    public Integer getField1() {
        return field1;
    }

    public void setField1(final Integer field1) {
        this.field1 = field1;
    }

    public Integer getField2() {
        return field2;
    }

    public void setField2(final Integer field2) {
        this.field2 = field2;
    }

    public Integer getField3() {
        return field3;
    }

    public void setField3(final Integer field3) {
        this.field3 = field3;
    }

    public void myList_change() {

        if (selectedItem < 1) {
            setField1(null);
        }
        if (selectedItem < 2) {
            setField2(null);
        }
        if (selectedItem < 3) {
            setField3(null);
        }
    }

    public void submit() {
    }
}

In the xhtml I

  • Removed the field1 field2 field3 from the process attribute since the seemed useless (submission takes place on the 'submit', no ajax submit needed).
  • Also removed them from the update attribute (the change of a field becoming required after a change does not need an update)
  • Changed the actionListener on the p:commandButton to an `action

XHTML code

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:p="http://primefaces.org/ui">
<h:head>
    <title>Example</title>
</h:head>
<h:body bgcolor="white">
    <h:form id="myForm">
        <p:messages showDetail="true" />
        <p:selectOneMenu id="myList" value="#{myBean.selectedItem}">
            <f:selectItem itemLabel="1" itemValue="1" />
            <f:selectItem itemLabel="2" itemValue="2" />
            <f:selectItem itemLabel="3" itemValue="3" />
            <p:ajax listener="#{myBean.myList_change}"
                process="myList"/>
        </p:selectOneMenu> <br/> 

        <p:inputNumber id="field1" value="#{myBean.field1}" required="true" decimalPlaces="0" />
        <p:inputNumber id="field2" value="#{myBean.field2}" required="#{myBean.selectedItem gt 1}" decimalPlaces="0" />
        <p:inputNumber id="field3" value="#{myBean.field3}" required="#{myBean.selectedItem gt 2}" decimalPlaces="0" />

        <p:commandButton value="submit" action="#{myBean.submit}" update="@form"/>

    </h:form>
</h:body>
</html>

And then for me it started 'working'. In the chat, it became ovbious that OP had the fixed list of fields in the process and update attributes for a specific reason.

  • In the update attribute so when the select was changed from e.g. 3 to 2, the 3rd field could be cleared (set in the bean), requiring an 'upate' to make it empty.
  • In the process attribute so when the select was changed to e.g. 3, the 2nd field (not set/cleared in the bean) would be submitted AND updated. If it was not in the process attribute, the original value from the bean would be put back in, effectively overriding the value the user might have typed in.

But this combination was the cause of weird behaviour, since if something is processed, it is validated too (in this case related to something being required or not). Having three empty fields, setting the select to 3 and submitting all three fields will fail since 2 are empty but the '3' won't be put in the model either then. I would have suspected the resetValues to help out here too but it did not (does not for me either). But what OP effectively wanted is to conditionally update an input field. The condition for this is already in the bean and you can simply add the update of the specific field that changes there too

public void myList_change() {

    if (selectedItem < 1) {
        setField1(null);
    }
    if (selectedItem < 2) {
        setField2(null);
        PrimeFaces.current().ajax().update("myForm:field2");
    }
    if (selectedItem < 3) {
        setField3(null);
        PrimeFaces.current().ajax().update("myForm:field3");
    }
}

There is totally no need for the field1, 2 and 3 to be in either the process or update attribute and all works as required (pun intended)

Kukeltje
  • 12,223
  • 4
  • 24
  • 47