2

I have in my JSF 2.2 + PrimeFaces application a wizard with three tabs. In each tab, I have a form to capture data from the user. Each of this form have some validations. Right now, when I click on next to go to the next tab, it is validating the data from the form. I don't want this to happen.

The user is persisting data in the data in one tab and then they want to keep moving forward. There is NO requirement to have data on the form to move to the next tab.

I was able to fix this by NOT having client side validation (i.e: not using the required word on the input fields) and validating on the backend only. The problem is I want the client side validation.

My wizard looks like this:

<h:form id="wizard">

    <p:wizard flowListener="#{afiliadoController.onFlowProcess}" nextLabel="Siguiente" 
    backLabel="Anterior" showStepStatus="true">

    <p:tab id="afiliadoTab" title="Afiliado">

            <p:growl autoUpdate="true"/>

            <p:panel styleClass="panels" id="panelAfiliado" style="margin-bottom:1em;" >

                <p:focus context="panelAfiliado"/>

                <h1> Agregue un Afiliado </h1>

                <h:panelGrid columns="4" styleClass="panelGrid" >

                    <p:outputLabel for="nombres" value="Nombres:" />

                    <p:inputText id="nombres" value="#{afiliadoController.afiliado.nombre}"
                    requiredMessage="Debe insertar un nombre." />

                    <p:outputLabel for="apellidos" value="Apellidos:" />

                    <p:inputText id="apellidos" value="#{afiliadoController.afiliado.apellido}"
                    requiredMessage="Debe insertar un apellido." />

                    <p:outputLabel for="estadoCivil" value="Estado Civil:" />

                    <p:selectOneMenu id="estadoCivil" effect="drop" value="#{afiliadoController.afiliado.estado_civil}" 
                    requiredMessage="Debe seleccionar un estado civil." >

                        <f:selectItem itemLabel="Estado Civil" itemValue=""/>
                        <f:selectItem itemLabel="Soltero" itemValue="S"/>
                        <f:selectItem itemLabel="Casado" itemValue="C"/>
                        <f:selectItem itemLabel="Union Libre" itemValue="U"/>
                        <f:selectItem itemLabel="Divorciado" itemValue="D"/>
                        <f:selectItem itemLabel="Viudo" itemValue="V"/>

                    </p:selectOneMenu>

                    <p:outputLabel for="direccion" value="Direccion:" />

                    <p:inputText id="direccion" value="#{afiliadoController.afiliado.direccion}"/>

                    <p:outputLabel for="telefono" value="Telefono:" />

                    <p:inputMask id="telefono" value="#{afiliadoController.afiliado.telefono}" mask="(999) 999-9999" 
                    requiredMessage="Debe insertar un telefono." />

                    <p:outputLabel for="fechaNacimiento" value="Fecha de Nacimiento:"/>

                    <p:calendar id="fechaNacimiento" yearRange="c-100:c" pattern="dd/MM/yyyy" navigator="true" 
                    value="#{afiliadoController.afiliado.fecha_nacimiento}" 
                    requiredMessage="Debe insertar su fecha de nacimiento." showOn="button" 
                    readonly="#{facesContext.currentPhaseId.ordinal eq 6}">

                        <p:ajax event="dateSelect" listener="#{afiliadoController.dateSelect}" update="edadAfi"/>

                    </p:calendar>

                    <p:outputLabel for="plan" value="Plan:" />                  
                    <p:selectOneMenu id="plan" effect="drop" value="#{afiliadoController.afiliado.plan}" 
                    requiredMessage="Debe seleccionar un plan." >

                        <f:selectItem itemLabel="Seleccione un plan" itemValue=""/>
                        <f:selectItem itemLabel="Vital Base" itemValue="1"/>
                        <f:selectItem itemLabel="Vital Elite" itemValue="2"/>
                        <f:selectItem itemLabel="Plan Vital Elite Internacional" itemValue="3"/>

                    </p:selectOneMenu>

                    <h:outputText value="Fecha: #{of:formatDate(now, 'dd/MM/yyyy')}"/>

                    <h:outputText value="Edad Afiliado: #{afiliadoController.afiliado.edad}" id="edadAfi"/>

                    <p:outputLabel for="modalidad" value="Modalidad:" />                    
                    <p:selectOneMenu id="modalidad" effect="drop" value="#{afiliadoController.afiliado.modalidad}" 
                    requiredMessage="Debe seleccionar una modalidad." required="true">

                        <f:selectItem itemLabel="Seleccione una modalidad" itemValue=""/>
                        <f:selectItem itemLabel="Solo Titular Del Contrato" itemValue="A"/>
                        <f:selectItem itemLabel="Titular del Contrato + Grupo Familiar" itemValue="B"/>

                        <f:validateBean disabled="#{!request.getParameter('validate')}" />

                    </p:selectOneMenu>

                    <p:commandButton value="Insertar" icon="fa fa-save" process="panelAfiliado" update="afiliadoTable"
                     actionListener="#{afiliadoController.insertAfiliado}" >

                        <f:param name="validate" value="true"/>

                     </p:commandButton>

                </h:panelGrid>         

            </p:panel>

    </p:tab>

    </p:wizard>

</h:form>

It is on purpose I am displaying only the first tab to make it more readable. On this first tab I have two <h:form> inside the main <h:form> (I am not sure if this is good practice.)

I am doing this because I want the enter keyword to work separately on each form. The <h:form> with id afiliadoTab has some validations. I want these validations to happen only when I click on the button:

<p:commandButton value="Insertar" icon="fa fa-save" process="panelAfiliado" update=":wizard:tablaAfiliados:afiliadoTable"
                         actionListener="#{afiliadoController.insertAfiliado}" validateClient="true"/>

I don't want these validations to happen when I click on NEXT. What can I do? (I know there are a few similar questions out there in SO but I couldn't quite get the answer. For example, the one below is using custom widget vars. I tried doing that but I found a few issues:

  1. How can I display the back button only when needed? (Not display it on the first tab)

  2. The validations are still in place, even when I use @this.

What are my best options?

Required Validation PrimeFaces wizard

Cœur
  • 37,241
  • 25
  • 195
  • 267
Erick
  • 823
  • 16
  • 37
  • A caution to anyone who is considering clicking that link above: **don't do it**. It's a rabbit hole into a dark abyss of bountiful answers all linked from one to the next from which you will never escape. – Andrew Apr 11 '17 at 19:52

3 Answers3

2

You have nested forms. Every <h:form> in a tab is inside other <h:form> outside <p:wizard>. You don't need separate forms, just the one outside wizard.

EDIT:

There is some workaround for this which works for me (don't validate field after clicking NEXT, but validates after clicking my button to validate).

For the fields you don't want to validate on NEXT add this tag (for example for <h:inputText>)

<f:validateBean disabled="#{!request.getParameter('validate')}" /> and to your button add this tag <f:param name="validate" value="true"/>.

Geinmachi
  • 1,251
  • 1
  • 8
  • 20
  • But do you understand my situation? I want to be able to do client side validation on every form and be able to click on NEXT to go to next tab – Erick Sep 09 '15 at 18:00
  • hmm so the idea is that the validation will only be activated once I click on a certain button? – Erick Sep 10 '15 at 21:32
  • Isn't it what you wanted saying " I want these validations to happen only when I click on the button"? – Geinmachi Sep 10 '15 at 21:42
  • That is correct but it is not working. I added to one of my fields. And then I added to the button on my form. I left the field blank and clicked on NEXT, and it didn't let me keep going because of the validations. – Erick Sep 10 '15 at 21:43
  • If you added this to only 1 field then the other fields which have validations will still block going forward, try adding this to every field with validation. – Geinmachi Sep 10 '15 at 21:45
  • I added the required="true" only to that field. The other fields are not required. The validation is activating only for that one field and it shouln't – Erick Sep 10 '15 at 21:46
  • It works for me because I use `@NotNull` annotation instead of `required=true`. – Geinmachi Sep 10 '15 at 21:54
  • ok I updated the code above. Take a look at the command button and take a look at the field with id="modalidad". This field has requried="true". When I click on next, it is showing the error message – Erick Sep 10 '15 at 21:59
  • I see the issue now, see my previous comment. You can try using JSR validation `@NotNull` instead of JSF validation `required="true"`. – Geinmachi Sep 10 '15 at 22:08
  • Take a look at my answer below. Not sure if my version is more efficient than your first proposal. – Erick Sep 10 '15 at 22:09
1

I finally figured out a way of doing it thanks to @Geinmachi and @BaulusC post here

So this is the updated code:

<p:panel styleClass="panels" id="panelAfiliado" style="margin-bottom:1em;" >

<p:focus context="panelAfiliado"/>

<h1> Agregue un Afiliado </h1>

<h:panelGrid columns="4" styleClass="panelGrid" >

  <p:outputLabel for="nombres" value="Nombres:" />

  <p:inputText id="nombres" value="#{afiliadoController.afiliado.nombre}"
  requiredMessage="Debe insertar un nombre." required="#{request.getParameter('validate')}"/>

  <p:outputLabel for="apellidos" value="Apellidos:" />

  <p:inputText id="apellidos" value="#{afiliadoController.afiliado.apellido}"
  requiredMessage="Debe insertar un apellido." required="#{request.getParameter('validate')}"/>

  <p:outputLabel for="estadoCivil" value="Estado Civil:" />

  <p:selectOneMenu id="estadoCivil" effect="drop" value="#{afiliadoController.afiliado.estado_civil}" 
  requiredMessage="Debe seleccionar un estado civil." required="#{request.getParameter('validate')}">

    <f:selectItem itemLabel="Estado Civil" itemValue=""/>
    <f:selectItem itemLabel="Soltero" itemValue="S"/>
    <f:selectItem itemLabel="Casado" itemValue="C"/>
    <f:selectItem itemLabel="Union Libre" itemValue="U"/>
    <f:selectItem itemLabel="Divorciado" itemValue="D"/>
    <f:selectItem itemLabel="Viudo" itemValue="V"/>

  </p:selectOneMenu>

  <p:outputLabel for="direccion" value="Direccion:" />

  <p:inputText id="direccion" value="#{afiliadoController.afiliado.direccion}" required="#{request.getParameter('validate')}"/>

  <p:outputLabel for="telefono" value="Telefono:" />

  <p:inputMask id="telefono" value="#{afiliadoController.afiliado.telefono}" mask="(999) 999-9999" 
  requiredMessage="Debe insertar un telefono." required="#{request.getParameter('validate')}"/>

  <p:outputLabel for="fechaNacimiento" value="Fecha de Nacimiento:"/>

  <p:calendar id="fechaNacimiento" yearRange="c-100:c" pattern="dd/MM/yyyy" navigator="true" 
  value="#{afiliadoController.afiliado.fecha_nacimiento}" 
  requiredMessage="Debe insertar su fecha de nacimiento." showOn="button" 
  readonly="#{facesContext.currentPhaseId.ordinal eq 6}" required="#{request.getParameter('validate')}">

    <p:ajax event="dateSelect" listener="#{afiliadoController.dateSelect}" update="edadAfi"/>

  </p:calendar>

  <p:outputLabel for="plan" value="Plan:" />              
  <p:selectOneMenu id="plan" effect="drop" value="#{afiliadoController.afiliado.plan}" 
  requiredMessage="Debe seleccionar un plan." required="#{request.getParameter('validate')}">

    <f:selectItem itemLabel="Seleccione un plan" itemValue=""/>
    <f:selectItem itemLabel="Vital Base" itemValue="1"/>
    <f:selectItem itemLabel="Vital Elite" itemValue="2"/>
    <f:selectItem itemLabel="Plan Vital Elite Internacional" itemValue="3"/>

  </p:selectOneMenu>

  <h:outputText value="Fecha: #{of:formatDate(now, 'dd/MM/yyyy')}"/>

  <h:outputText value="Edad Afiliado: #{afiliadoController.afiliado.edad}" id="edadAfi"/>

  <p:outputLabel for="modalidad" value="Modalidad:" />              
  <p:selectOneMenu id="modalidad" effect="drop" value="#{afiliadoController.afiliado.modalidad}" 
  requiredMessage="Debe seleccionar una modalidad." required="#{request.getParameter('validate')}">

    <f:selectItem itemLabel="Seleccione una modalidad" itemValue=""/>
    <f:selectItem itemLabel="Solo Titular Del Contrato" itemValue="A"/>
    <f:selectItem itemLabel="Titular del Contrato + Grupo Familiar" itemValue="B"/>

    <!-- <f:validateBean disabled="#{!request.getParameter('validate')}" /> -->

  </p:selectOneMenu>

  <p:commandButton value="Insertar" icon="fa fa-save" process="panelAfiliado" update="afiliadoTable"
   actionListener="#{afiliadoController.insertAfiliado}" >

    <f:param name="validate" value="true"/>

   </p:commandButton>

</h:panelGrid>       

Take a look at all of my fields. They all have this:

required="#{request.getParameter('validate')}"

If you take a look at my button, it has:

<f:param name="validate" value="true"/>

This way, when I click on the NEXT button on my wizard, it WON'T activate the validations. The validations will ONLY activate when I click the button on that particular form!

I hope this helps someone some day.

Community
  • 1
  • 1
Erick
  • 823
  • 16
  • 37
1

So if someone will have validation problem similar to this there are two cases:

  1. If you use JSF validation like PrimeFaces required attribute or normal tags like <f:validateRequired/> you can do required = "#{!request.getParameter('validate')}" and <f:validateRequired disabled="#{!request.getParameter('validate')}"/> like OP or
  2. If you use JSR Bean validation like @NotNull you can do <f:validateBean disabled="#{!request.getParameter('validate')}" />

to disable validation with <f:param name="validate" value="true"/> to an action button.

Geinmachi
  • 1,251
  • 1
  • 8
  • 20