0 Replies Latest reply on Jul 21, 2009 5:51 AM by Arbi Sookazian

    Seam validator for live duplicate validation

    Arbi Sookazian Master

      I have three HtmlSelectOneMenu components (say Foo 1, Foo 2, Foo 3).


      I want to implement live vaildation using a Seam validator (see below for example from Seam ref doc) to warn user if they select an already inputted value for another instance of the Foo drop-down control.  For example, if they select Best Buy for Foo 1 and Best Buy for Foo 2 and then tab out of Foo 2 field, I want to display an error msg like duplicates not allowed on Foo 2 field.  I want this to happen for onblur event (a4j:support) as well as on submission of the form.  I also want to show warning msg when user enters a value that is already saved to the table for this entity (but that's another problem altogether really and not relevant to this thread).


      @Name("itemValidator")
      @BypassInterceptors
      @org.jboss.seam.annotations.faces.Validator
      public class ItemValidator implements javax.faces.validator.Validator {
           public void validate(FacesContext context, UIComponent cmp, Object value) throws ValidatorException {
                 ItemController ItemController = (ItemController) Component.getInstance("itemController");
                 boolean valid = itemController.validate(value);
                 if (!valid) {
                     throw ValidatorException("Invalid value " + value);
                 }
           }
      }
      <h:inputText value="#{shop.item}" validator="itemValidator" />



      What is the best way to implement this?


      The problem I'm having is that in the validate() method below, I have access to only the value of the current field, which is the field the focus was on when the onblur event fired.  I need to have access to the values for all three HtmlSelectOneMenu components in the same method.  I tried to inject the backing bean into my FooValidator class but that only gives me null references to the HtmlSelectOneMenu components.


      So I found this code from this link:


      public static UIComponent findComponentInRoot(String id) {
          UIComponent component = null;
      
          FacesContext facesContext = FacesContext.getCurrentInstance();
          if (facesContext != null) {
            UIComponent root = facesContext.getViewRoot();
            component = findComponent(root, id);
          }
      
          return component;
      }
          
      public static UIComponent findComponent(UIComponent base, String id) {
          if (id.equals(base.getId()))
            return base;
        
          UIComponent kid = null;
          UIComponent result = null;
          Iterator kids = base.getFacetsAndChildren();
          while (kids.hasNext() && (result == null)) {
            kid = (UIComponent) kids.next();
            if (id.equals(kid.getId())) {
              result = kid;
              break;
            }
            result = findComponent(kid, id);
            if (result != null) {
              break;
            }
          }
          return result;
      }



      But I'm not sure if this is even a best practice approved approach to solve this problem for a JSF/Seam app.  It returns HtmlSelectOneMenu as the UIComponent instance but I really want the underlying entity class (I'm using the <s:convertEntity> tag) so I can exec the relevant getter method on the entity class to have the data I need to compare for duplicates.


      Any tips would be appreciated.  This use case (validation of multiple replicated input fields in a JSF form) is not very common in Seam apps that I've seen so far...