6 Replies Latest reply on Jul 2, 2006 5:07 PM by supernovasoftware.com

    How to access datatable row from a converter.  All is code p

      I am trying to validate a list of entry by using the EntityManager to query the database to check limits. I can also see myself using this for a list of
      tracking numbers like on UPS as well. For each I would like to hit the DB and see if it meets my criteria and give a meaninful message to the user next to each field. I have this working and the EntityManager is accessable from my Converter, but I need additional information besides this value to do my checks in this case. It is present in the DataModel that this field is coming from, but I am unsure of the best way to get accesss to this once inside the converter. Any advice would be greatly appreciated.

      As a minimum for this case I need to know the pk of the item in question so I can check its balance in the DB.

      I have posted both the Java and Facelets code below with some comments.

      Java Code

      @Stateless
      @Name("genericItemNumPieces")
      @Interceptors(SeamInterceptor.class)
      public class GenericItemNumPiecesConverterBean implements GenericItemNumPiecesConverterInterface
      {
       @Logger private Log log;
      
       @PersistenceContext
       EntityManager em;
      
       public Converter getConverter() {
       return new GenericItemNumPiecesConverter(em, log);
       }
      
       public static class GenericItemNumPiecesConverter implements Converter {
      
       private EntityManager em;
       private Log log;
      
       public GenericItemNumPiecesConverter(EntityManager em, Log log) {
       this.log=log;
       this.em=em;
       }
      
       public String getAsString(FacesContext facesContext, UIComponent component, Object obj) {
       if (obj == null) return null;
       Long numPieces = (Long) obj;
       String val = Long.toString(numPieces);
       return val;
       }
      
       public Object getAsObject(FacesContext facesContext, UIComponent component, String str)
       throws ConverterException {
       // This is just to test the log and entity manager.
       // But I need to access the row of the datatable this component is in
       // so that I can verify the quantity.
       List list = em.createQuery("from Size s").getResultList();
       log.info("There are " + list.size() + " sizes");
      
       // Check if the value entered is parsable as a Long. If not it throws ConverterException
       Long numPieces = parseLong(str);
      
       // Check if quantiy is positive.
       if (numPieces < 0) throwConverterException();
      
       // What I would like to do here is verify against the DB to see if the quantity is <= the number
       // of pieces available and throw new ConverterException(new FacesMessage("The number of pieces must be <= X"))
       // How can I access the row from the datatable that this component is located in so that I can
       // check the quantity for the proper line item?
       return numPieces;
       }
       }
      
       private static Long parseLong(String str) {
       Long numPieces = null;
       try {
       numPieces = Long.valueOf(str).longValue();
       } catch (NumberFormatException e) {
       throwConverterException();
       }
       return numPieces;
       }
      
       private static void throwConverterException() {
       throw new ConverterException(new FacesMessage("The number of pieces must be >= 0."));
       }
      }
      


      Facelets code
      <t:dataTable id="#{dataTableId}"
       var="couplingL"
       rowIndexVar="rowIndex"
       rows="20" preserveSort="true"
       value="#{couplingList}">
       <t:column styleClass="paddlr alignr">
       <f:facet name="header">Qty Rec</f:facet>
       <h:inputText id="recList_#{rowIndex}"
       value="#{couplingL.numPiecesInput}"
       converter="#{genericItemNumPieces.converter}" />
       <h:messages for="recList_#{rowIndex}" />
       </t:column>
       </t:dataTable>
      


        • 1. Re: How to access datatable row from a converter.  All is co
          pmuir

          I would suggest using a Validator to do validation, not a Converter (concept clarity, aviods a pointless getAsString() method).

          A few ideas:

          I'm not sure you can get anything except the value in this case - the component and its parent (etc) are of no use. My best suggestion is to write a Facelets ValidateHandler. This would then allow you to do

          <my:validateLimit pk="#{couplingL.id}" />


          Then within the ValidateHandler create a new validator with a field containing the pk. This could then be looked up using a EL or Seam component accessed programmatically or JNDI or ... BUT I don't know how well this will play with components in datatables.

          Another way to do the same is to make validateLimit a non-rendering component (with componenthandler) and have it attach a validator to its parent with the pk field set (which I recall having more luck with than the above due to state saving stuff).

          Or perhaps you could do the validation inside a backing bean access method when the whole datatable is available, but then you have problems attaching the FacesMessage onto the correct table row.

          It would be good if you reach a solution if you share it :)

          • 2. Re: How to access datatable row from a converter.  All is co

            Thank you very much for your response.

            I always share any success I have. These forums are you vital to my progress, and I feel the responsibility to post my results in order to help others.

            I am also posting on the facelets list. As far as JSF stuff goes they provide awesome answers there.

            I will post my progress. Keep an eye on this thread. I am sure that I will need some guidance to reach a quality solution.

            To me this is key. I already have access to the EntiyManager in the Converter which I will change to a Validator. I was wonder what getAsString was doing in this case anyway.

            Validating against the DB will open up all kinds of possibilities for me.

            • 3. Re: How to access datatable row from a converter.  All is co
              pmuir

              For your 'limit' validator you could go pass in the limit value directly to your custom validator:

              <my:validateLimit limit="#{backingBean.limit}" />


              - again how it works with dataTable I'm not sure.

              The trouble with the inbuilt:

              <f:validateLongRange minimum="0" maximum="#{backingBean.limit}" />


              is that (with MyFaces at least) is that the attributes of the f:validate* tags is afaik they don't accept EL.

              This doesn't address your more general requirement of passing other attributes to a validator; but is it a 'better' way to do it? I don't know.

              • 4. Re: How to access datatable row from a converter.  All is co

                Can I pass in an object?

                I switched to Validator an the code is way cleaner. See below. I was able to remove the inner class which I did not like.

                The only side effect that I am not fond of is that JSF intercepts the validation if something other than a number is entered.

                Before my message was consistent. Now I get the generic JSF message in the case. Can I bypass and handle this myself?

                I will continue to consider the a solution, but I need to do something visible. My client just does not understand. "I spent all day trying to set up a validation."

                @Stateless
                @Name("genericItemNumPieces")
                @Interceptors(SeamInterceptor.class)
                public class GenericItemNumPiecesConverterBean implements GenericItemNumPiecesConverterInterface
                {
                 @Logger
                 private Log log;
                
                 @PersistenceContext
                 EntityManager em;
                
                 @In(create = true)
                 FacesMessages facesMessages;
                
                 private void addMessage(UIComponent component)
                 {
                 ((UIInput) component).setValid(false);
                 facesMessages.add(component.getId(), new FacesMessage("The number of pieces must be >= 0."));
                 }
                
                 public void validate(FacesContext facesContext, UIComponent component, Object obj) throws ValidatorException
                 {
                 Long numPieces = (Long) obj;
                 if (numPieces < 0) addMessage(component);
                 }
                }
                



                • 5. Re: How to access datatable row from a converter.  All is co

                  I tried the following, but I get the value must be between 0 and 0. I am assuming the you are correct an the el is being ignored.

                  In my opinion this should be a nobrainer. Maybe I am overlooking some obvious solution.

                  Has anyone else attempted to do this?

                  <h:inputText id="recId" value="#{couplingL.numPiecesInput}" required="true" class="input_std w50">
                   <f:validateLongRange minimum="0" maximum="#{couplingL.numPieces}"/>
                   </h:inputText>


                  • 6. Re: How to access datatable row from a converter.  All is co

                    I apologize for so many posts, but I feel my usecase may help someone to provide me with a solution, and this does seem like something that others either would desire or have already implemented.

                    In my application I provide a list of available items that can be committed to a work order for a manufacturing process.

                    There are multiple users and if some of the items are committed to another workorder while the first user is doing their input they may request more items than are currently now available.

                    When the page is summited if validation fails the new values are reflected in the list if the underlying data has changed. This is exactly what I want.

                    Now I just need to validate against the live dataset and return a meaninful message to the user.