6 Replies Latest reply on Sep 14, 2009 4:49 PM by trunikov.dmitry.trunikov.zoral.com.ua

    Seam Validation losing state

    lucianom86
      Good Morning,

      I have a little application using Seam and until now all my validations had been done directly by hibernate validator using anotations in my entities. Now i need to do a validation programatically in the Action. When the error occours, the error message is displayed correctly, but it loses the data informed by the user in the form. When occours some simple validation error (@NotNull for example) it comes back to the form with the data filled correctly, the problem is with the manual validation.

      My form on xhtml looks like this



      `

                      <h:form id="formUsuario" styleClass="edit">
                              <s:validateAll>
                                      <rich:panel>
                                              <f:facet name="header">
                                                      <h:outputText value="#{messages[usuarioAction.keyLabelAct]}" />
                                              </f:facet>
                                             
                                              <s:decorate id="loginField" template="../layout/edit.xhtml">
                                                      <ui:define name="label">#{messages['label.login']}</ui:define>
                                                      <h:inputText value="#{usuarioAction.usuario.login}" id="txtLogin" required="true" size="15" />
                                              </s:decorate>
                                             
                                              <s:decorate id="nomeField" template="../layout/edit.xhtml">
                                                      <ui:define name="label">#{messages['label.nome']}</ui:define>
                                                      <h:inputText value="#{usuarioAction.usuario.nome}" id="txtNome" required="true" size="30" />
                                              </s:decorate>
                                             
                                              <s:decorate id="perfilField" template="../layout/edit.xhtml">
                                                      <ui:define name="label">#{messages['label.perfil']}</ui:define>
                                                      <h:selectOneMenu id="selectPerfil" value="#{usuarioAction.usuario.perfil}" required="true"
                                                              converter="#{usuarioAction.converterPerfil}">
                                                              <s:selectItems var="perfil" value="#{usuarioAction.perfis}" label="#{perfil.nome}"
                                                                      noSelectionLabel="#{messages['label.selecione']}"/>                                         
                                                      </h:selectOneMenu>
                                              </s:decorate>
                                             
                                              <div style="clear:both">
                                                      <span class="required">*</span>
                                                      #{messages['label.camposObrigatorios']}
                                              </div>
                                     
                                      </rich:panel>
                                     
                                      <div class="actionButtons">
                                              <h:commandButton action="#{usuarioAction.add}" id="btnAdd" value="#{messages['label.incluir']}"
                                                      rendered="#{usuarioAction.act eq 'incluir'}"/>
                                              <h:commandButton action="#{usuarioAction.update}" id="btnUpdate" value="#{messages['label.alterar']}"
                                                      rendered="#{usuarioAction.act eq 'alterar'}"/>
                                              <h:commandButton action="#{usuarioAction.remove}" id="btnRemove" value="#{messages['label.excluir']}"
                                                      rendered="#{usuarioAction.act eq 'excluir'}"/>
                                              <s:button view="/usuario/list.xhtml" value="#{messages['label.cancelar']}" id="btnCancel" propagation="end"/>
                                      </div>
                                     
                              </s:validateAll>
                      </h:form>
      ...
      `

      My Action:
      `...
      @Name("usuarioAction")
      public class UsuarioAction extends BaseAction {

              private Usuario usuario = new Usuario();
             
              @In("#{usuarioService}")
              private UsuarioService usuarioService;
             
              @In("#{perfilService}")
              private PerfilService perfilService;
             
              @In
              private FacesMessages facesMessages;
             
              private EntidadeComIdConverter<Perfil> converterPerfil;
             
              private List<Perfil> perfis;
             
              @Begin(join = true)
              public String loadUsuario() {
                      if(usuario != null && usuario.getIdUsuario() != null &&
                                      (getAct().equals(ACTION_ALTERAR) || getAct().equals(ACTION_EXCLUIR))) {
                              usuario = usuarioService.findById(usuario.getIdUsuario());
                              if(usuario == null) {
                                      facesMessages.add(FacesMessage.SEVERITY_ERROR, "#{messages['label.usuario.naoEncontrado']}");
                                      return NOT_FOUND;
                              }
                      } else {
                              usuario = new Usuario();
                      }
                      return SUCCESS;
              }
             
              @End
              public String add() {
                      if(usuarioService.existeUsuarioComLogin(usuario.getLogin())) {
                              facesMessages.addToControl("txtLogin", "#{messages['erro.existe.usuario.login']}");
                              return null;
                      }
                     
                      usuarioService.add(usuario);
                      facesMessages.add("#{messages['label.usuario.inserido']}");
                      usuario = new Usuario();
                      return SUCCESS;
              }
      ..
      `

      My navigation rules:   

      <?xml version="1.0" encoding="UTF-8"?>
      <page xmlns="http://jboss.com/products/seam/pages"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.1.xsd">
         
          <param name="idUsuario" value="#{usuarioAction.usuario.idUsuario}"/>
          <param name="act" value="#{usuarioAction.act}"/>
          <action execute="#{usuarioAction.loadUsuario}"/>
             
              <navigation>
                      <rule if-outcome="notFound">
                              <redirect view-id="/usuario/list.xhtml"/>
                      </rule>          
              </navigation>
             
              <navigation from-action="#{usuarioAction.add}">
                      <rule if-outcome="success">
                              <redirect view-id="/usuario/list.xhtml"/>                
                      </rule>
              </navigation>
             
              <navigation from-action="#{usuarioAction.update}">
                      <redirect view-id="/usuario/list.xhtml"/>
              </navigation>    
       
              <navigation from-action="#{usuarioAction.remove}">
                      <redirect view-id="/usuario/list.xhtml"/>
              </navigation>     
             
      </page>


      Can someone figure out what can be causing the problem?I've already tried many thins, but no success.
      Thanks,
      Luciano

      Ps.: Sorry for the english and for the text formating.
        • 1. Re: Seam Validation losing state
          cash1981

          I dont see any custom validators.


          for instance:




          @Name("validator")
          @BypassInterceptors
          public class Validator {
          
               private String datePattern;
          
               /**
                * Validates a date. Will *not* do any conversion. Takes a string
                * 
                * @param context
                * @param toValidate
                * @param value
                */
               public void date(FacesContext context, UIComponent toValidate, Object value) {
                    String val = (String) value;
                    SimpleDateFormat sdf = new SimpleDateFormat(datePattern);
                    Date nDate;
                    try {
                         nDate = sdf.parse(val);
                         if (nDate.after(new Date())) {
                              ((UIInput) toValidate).setValid(false);
                              FacesMessages.instance().addToControlFromResourceBundle(toValidate.getId(), "future.date", datePattern);
                         }
                    } catch (ParseException ex) {
                         ((UIInput) toValidate).setValid(false);
                         FacesMessages.instance().addToControlFromResourceBundle(toValidate.getId(), "invalid.date", datePattern);
                    }
          
               }
          }
          



          And in your xhtml just add the validator on the inputtextfield you want to validate ie:



          <h:inputText value="#{mybean.date}" required="true" validator="#{validator.date}" immediate="true"/>




          That should work. If this still doesnt work, its because you to set higher scope on your component. Default scope is event, thus it will loose after render response. Try PAGE scope or higher.

          • 2. Re: Seam Validation losing state
            lucianom86
            Hi Shervin,

            Thanks for your answer. There's a custom validation on method add of the class UsuarioAction.

            @End
            public String add() {
                if(userService.existsUserWithLogin(user.getLogin())) {
                    facesMessages.addToControl("txtLogin", "#{messages['erro.existe.usuario.login']}");
                    return null;
                }
                usuerService.add(user);
                facesMessages.add("#{messages['label.usuario.inserido']}");
                usuario = new Usuario();
                return SUCCESS;
            }

            I've already tried to change the class scope to PAGE and SESSION, but the problem persists. I'll also try to use your solution.


            • 3. Re: Seam Validation losing state
              trunikov.dmitry.trunikov.zoral.com.ua

              I'm not sure but it seems that immediate cause of the issue is an annotation @End at the method add().
              That method ends conversation in any case either success or failure.
              Hope this will help you.


              P.S. From my point of view the best place for conversation management directives is *.page.xml files.

              • 4. Re: Seam Validation losing state
                lucianom86
                Hi Dmitry,

                In the Seam docs (http://docs.jboss.org/seam/2.0.2.GA/reference/en-US/html_single/#end-annotation) it says:
                "Specifies that a long-running conversation ends when this method returns a non-null outcome without exception."

                As my method is returning null when the validation error occours, it shouldn't be ending the conversation. Anyway, i'll try do it using the *page.xml instead of the annotations.

                Thanks,
                Luciano
                • 5. Re: Seam Validation losing state
                  cash1981

                  Dmitry Trunikov wrote on Sep 14, 2009 08:21:


                  P.S. From my point of view the best place for conversation management directives is *.page.xml files.


                  I disagree. I think it is much more readable to have these stuff on the java code, instead of toggling back and forth on pages.xml


                  • 6. Re: Seam Validation losing state
                    trunikov.dmitry.trunikov.zoral.com.ua

                    Shervin Asgari wrote on Sep 14, 2009 14:41:

                    I disagree. I think it is much more readable to have these stuff on the java code, instead of toggling back and forth on pages.xml


                    Tastes differ ;)
                    I can see one big advantage in conversation management through page.xml.
                    In the development mode we can easily change conversation's behaviour  without code change and application redeploying. It is very handy feature especially for newcomers of the Seam as they have a chance to play with conversations.
                    The second point (just my opinion) is that conversation has more relation to navigation than to code.