2

I am building a JSF form which includes a primefaces p:autoComplete component. The following is an extract from my xhtml page, showing the relvant information about the autoComplete component.

<p:autoComplete 
    value="#{curAttribute.value}" 
    completeMethod="#{newBacking.lookupActivated}"
    var="curEntry" 
    itemLabel="#{curEntry.classname}" 
    itemValue="#{curEntry.id}"
    emptyMessage="Start typing..."/>

Please notice that curAttribute is an instance of the CosmoAttribute class, and that the CosmoAttribute.value is a String (of course, CosmoAttribute has all the getters and setters for its fields).

The method newBacking.lookupActivated() returns a List<CosmoCard>.

CosmoCard.classname and CosmoCard.id are both Strings.

I know that I'm woirking with POJOs, but since all my values are String fields from a POJO, I don't think that I need a converter. Anyway, my autoComplete field works fine, but when i select an item, i get the following exception:

SEVERE:   Error Rendering View[/test.xhtml]
javax.el.PropertyNotFoundException: /test.xhtml @98,68 itemLabel="#{curEntry.id}": The class 'java.lang.String' does not have the property 'id'.
    at com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:111)
    at javax.faces.component.ComponentStateHelper.eval(ComponentStateHelper.java:194)
    at org.primefaces.component.autocomplete.AutoComplete.getItemLabel(AutoComplete.java:148)
    .
    .

Caused by: javax.el.PropertyNotFoundException: The class 'java.lang.String' does not have the property 'id'.
    at javax.el.BeanELResolver.getBeanProperty(BeanELResolver.java:730)
    at javax.el.BeanELResolver.getValue(BeanELResolver.java:351)
    at com.sun.faces.el.DemuxCompositeELResolver._getValue(DemuxCompositeELResolver.java:176)
    .
    .

Has anyone a clue of what am I doing wrong? You can find a very similar question here, but it was unfortunately unanswered. I am willing to provide more details about my code.

UPDATE:

Actually, everything is working fine: I see the correct value in the dropdown of the p:autocomplete. As i select a value, the data in the backing bean (newBacking) is updated accordingly. I just can't get rid of the exception, which, however, does not have any effect on the execution of my page.

I am respecting the constraints of the tag, i.e. the value attribute and the itemValue are of the same type (both Strings). The only thing that is not properly ok is that the system tries to convert a List in a List, I don't know why, or when, but the failure in the conversion (and the subsequent exception) does not have any effect on the behaviour of my page.

UPDATE:

Here is the link to a very simplified version of the project (netbeans). The relevant files of the project are also listed below.

test.xhtml

<h:body>
    <h:form id="form">  
        <p:dataTable var="curAttribute" value="#{newBacking.card.attributes}">
            <p:column >
                THE CURSED FIELD <br /><br />

                <p:autoComplete 
                    value="#{curAttribute.value}" 
                    completeMethod="#{newBacking.lookupActivated}"
                    var="curEntry" 
                    itemLabel="#{curEntry.code}" 
                    itemValue="#{curEntry.id}">
                </p:autoComplete>
            </p:column>
        </p:dataTable> 
    </h:form>    

newBacking.java

@Named()
@SessionScoped
public class NewBacking implements Serializable {

   private CosmoCard card;

   private String currentCardClassname = "";

   @PostConstruct
   public void init() {

       Random randomGenerator = new Random();
       card = new CosmoCard();
       card.setId("ID" + randomGenerator.nextInt(1000));

       CosmoAttribute myLA = new CosmoAttribute();
       myLA.setLabel("LookupAttributeLabel");
       myLA.setValue("LookupAttributeValue");
       card.getAttributes().add(myLA);
   }

   public CosmoCard getCard() {
      return card;
   }

   public String getCurrentCardClassname() {
      return currentCardClassname;
   }

   public void setCurrentCardClassname(String currentCardClassname) {
       this.currentCardClassname = currentCardClassname;
   }

   public List<CosmoCard> lookupActivated(String tgtQuery) {

       Logger.getLogger(NewBacking.class.getName()).info("[NewBacking.lookupActivated()] Query: " + tgtQuery);
       return CosmoCardList.generateCardList(10).getCards();
   }

}
Community
  • 1
  • 1
INElutTabile
  • 866
  • 2
  • 20
  • 38
  • @Pumpkin It seems that I cannot use the POJO approach for the following reasons: 1) The POJO (CosmoAttribute) which is supposed to be the value of the p:autocomplete tag has not the same type which is returned by the complete method (CosmoCard). 2) The complete method returns a List unless I use a converter. 3) Even if I use a converter, the system tries to put a CosmoCard in the place of a CosmoAttribute, when assigning the value. I'm starting to think that I will use a List as return type of the complete method. Maybe with a separator to distinguish the value from the label. – INElutTabile Nov 05 '14 at 14:02
  • 1
    In my opinion you should obviously return what you use. So the problem is due to your first reasoning which causes 2 and 3. Stuff like using a seperator and processing the string returned are not good practices. – Pumpkin Nov 05 '14 at 14:23
  • My thoughts exactly (about the separator stuff). It seems, then, that I am forced to use the same type of POJO both in the value of the p:autocomplete and in the return type of the complete method, right? So that I do not have any problem using a converter. I don't see any other solution. – INElutTabile Nov 05 '14 at 14:27
  • The only thing that needs to be of same type are the itemValue and value attributes. You can return class A from your complete method and say value = B and itemValue = A.getB(). Value attribute represents the bound instance in the backing bean and item value represents the value to be bound. Unfortunately it can only be a string, so in the event you are not going to bind a string you have to use a converter again in which the component will return the string representation of your object which will be found and replaced automatically in your converter as you specify in its related methods. – Pumpkin Nov 05 '14 at 14:50
  • Even though I am getting that exception, the behaviour of the component seems to be working, I can't figure out what is the problem! – INElutTabile Nov 05 '14 at 15:19
  • Submit a simplified reproducable version of your code as the exception could be caused by a number of things. – Pumpkin Nov 05 '14 at 15:28
  • Added more information about my code. In the current version the code deas not even render the page, same error: The class 'java.lang.String' does not have the property 'code'. – INElutTabile Nov 05 '14 at 16:21

1 Answers1

3

Your complete method is returning an instance of a complex class and thereby you need a converter. Without the existense of a converter, your component is thinking that its working with a string and trying to give reference to it when getting value and label attributes. Try using this and see if it works but I strongly recommend creating your own converter based on your needs:

@FacesConverter("anySelectConverter")
public class AnySelectConverter implements Converter{


     private static Map<Object, String> entities = new ConcurrentHashMap<Object, String>();

        @Override
        public String getAsString(FacesContext context, UIComponent component, Object entity) {

            // TODO : Fix
            if(entity == null)
                return "";

            synchronized (entities) {
                if (!entities.containsKey(entity)) {
                    String uuid = UUID.randomUUID().toString();
                    entities.put(entity, uuid);
                    return uuid;
                } else {
                    return entities.get(entity);
                }
            }
        }

        @Override
        public Object getAsObject(FacesContext context, UIComponent component, String uuid) {
            for (Entry<Object, String> entry : entities.entrySet()) {
                if (entry.getValue().equals(uuid)) {
                    return entry.getKey();
                }
            }
            return null;
        }

    }
Pumpkin
  • 1,993
  • 3
  • 26
  • 32
  • 1
    Will try to use a converter, I was actually looking for a way to avoid the use of a converter. Does primefaces only accept "simple" classes as output for the complete method? – INElutTabile Nov 05 '14 at 13:19
  • Accepts string only, thereby usage of converters become mandatory, the alternative requires you to do all that manually. I suggest creating an anySelectConverter for basic, rarely used components and custom converters for relatively more important components. – Pumpkin Nov 05 '14 at 13:27
  • Answered in the comment area of the question. – INElutTabile Nov 05 '14 at 14:02
  • You could use the Omnifaces [ListConverter](http://showcase.omnifaces.org/converters/ListConverter). See this question: [POJO support for primefaces autocomplete using omnifaces](http://stackoverflow.com/questions/21689074/pojo-support-for-primefaces-autocomplete-using-omnifaces) – Mark Nov 05 '14 at 14:39