p:autoComplete itemLabel throws “The class 'java.lang.String' does not have the property 'label'.”

别来无恙 提交于 2019-11-29 04:25:11
BalusC

You shouldn't feed it with List<SelectItem>. You should feed it with List<Pessoa>. You should also not concentrate on converting SelectItem. You should concentrate on converting the item value, which is Pessoa. The SelectItem is a leftover from the old JSF 1.x ages. In JSF 2.x this is not mandatory anymore, thanks to the var, itemValue and itemLabel attributes in the view. This keeps your bean clean from view-specific clutter.

The Converter is only necessary whenever you use itemValue="#{pessoa}" and the #{modeloPopupBuscaPessoa.itemSelecionado} refers a Pessoa property. You should then in getAsString() convert Pessoa to its unique String representation (so that it can be printed in HTML) and in getAsObject() convert from String to Pessoa (so that it can be set in bean property).

However, if #{pessoa.value} is a String and #{modeloPopupBuscaPessoa.itemSelecionado} is also a String, then you should just use itemValue="#{pessoa.value}" and remove the Converter altogether.

See also:

A generic Converter which you can use for Primefaces Auto-completes and all other purposes:

import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import java.util.WeakHashMap;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;

@FacesConverter(value = "entityConverter")
public class EntityConverter implements Converter {

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

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object entity) {
        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;
    }

}

One more easiest way to achieve this by:
overriding the toString() method in Pessoa Pojo class. This toString() should only return Label that you want to display.
If you use this method then there is NO NEED for converter.

For example:

public class Pessoa implements Serializable{

private String value;
private String label;

//Setter and getters

@Override
public void toString(){
return label;
}

}

Then you can use:

<p:autoComplete value="#{modeloPopupBuscaPessoa.itemSelecionado}"
                completeMethod="#{controladorSugestaoPessoa.atualizarSugestoes}"
                var="pessoa" itemLabel="#{pessoa}" itemValue="#{pessoa.value}"/>

This is the way i'm currently using and working well.

I faced the same issue and the author's comment at Primefaces autocomplete with POJO and String value gave me the hint to find the source of the problem in my case.

Overview

The problem is that value=#{objectValue} is of type String but the method referenced in completeMethod is returning a List<Object>.

The Design

I have the following POJOs (simplified):

public class CollaboratorGroup {
   private String groupId;

   private String groupName;

   private Collaborator piUserId;

   ...
}

and

public class Collaborator {
   private String userId;

   private String fullName;

   private String groupId;
   ...
}

Does not matter whether this is a useful design. I just want to address the issue.

The following p:autoComplete (simplified):

<p:autoComplete var="group"
    itemLabel="#{group.groupId}"
    itemValue="#{group.groupId}"
    completeMethod="#{bean.completeGroup}"
    value="#{collaborator.groupId}">
    <f:facet name="itemtip">
        <p:panelGrid columns="2">
            <f:facet name="header">
                <h:outputText value="#{group.groupId}" />
            </f:facet>
            <h:outputText value="Name:" />
            <h:outputText value="#{group.groupName}" />
            <h:outputText value="PI" />
            <h:outputText value="#{group.piUserId.fullName}" />
        </p:panelGrid>
    </f:facet>
</p:autoComplete>

will throw The class 'java.lang.String' does not have the property 'groupId'. When I change to itemLabel=#{group}, I will see the groupId CG00255 in the input field but many of org.coadd.sharedresources.model.CollaboratorGroup@... in the dropdown list. If I select one of these, this toString() value is set to Collaborator.groupId which is not desired.

The Problem's Source

I feed the p:autoComplete with a List<CollaboratorGroup> while Collaborator.groupId is a String and itemLabel is used to "format" both, the String groupId set as value="#{collaborator.groupId}" and the CollaboratorGroup that comes from the List, generated by completeMethod="#{bean.completeGroup}".

Possible Solutions

  1. You could adjust the Model by changing the member groupId to CollaboratorGroup in Collaborator if it does not destroy your design. In this case, especially as CollaboratorGroup has the member Collaborator piUserId.
  2. You could just fill the p:autoComplete with List<String> groupIdList but in this case you have to find a different solution for the itemtip.

  3. A very quick solution is to use itemLabel="#{group.class.simpleName eq 'String' ? group : group.groupId}" as mentionned at Primefaces autocomplete with POJO and String value.

    • Problems
      • You have to care about NullPointerExceptions.
      • You fill up your View with logic.
      • It is not a very flexible or dynamic design.
  4. Implement 3. in a bean method itemLabel="#{bean.printGroupId(group)}" where you have full control over the logic. This is what I did.

    public String printGroupId(Object group) {
        if (group == null) return null;
        return (group instanceof String) ? (String) group : (group instanceof CollaboratorGroup) ? ((CollaboratorGroup) group).getGroupId() : null;
    }
    

    (Not the best, just to give you an idea.)

Esk Srikanth
 ELContext elContext = FacesContext.getCurrentInstance().getELContext();
 ItemBean itemBean = (ItemBean) elContext.getELResolver().getValue(elContext, null, "itemBean");
 for(Item item : itemBean.getItems()){
     if(item.getId().getItemCode().equals(value)){
         return item;
     }
 }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!