'Primefaces autoComplete component with different value type for suggestions and component value
I'm not sure if this is possible ... but I'd like to use the autoComplete component, where the value attribute is of type String and where the completeMethod returns a List of some heavy object.
It is also a requirement for me to use forceSelection="false"
This is what I think should work (but doesn't):
<p:autoComplete id="it_demandeur"
value="#{utilisateurDemandeurCtrl.critereRechercheDemandeur}"
var="demandeurItem"
itemLabel="#{demandeurItem ne null ? demandeurItem.numeroOW.toString().concat(' - ').concat(demandeurItem.nom) : ''}"
itemValue="#{demandeurItem.nom}"
completeMethod="#{utilisateurDemandeurCtrl.completeDemandeur}"
minQueryLength="3"
cacheTimeout="10000">
<p:column>
#{demandeurItem.numeroOW} - #{demandeurItem.nom}
</p:column>
</p:autoComplete>
This is the method that returns the list of suggestions:
@SuppressWarnings("unchecked")
public List<Demandeur> completeDemandeur(String query) {
StringBuilder jpql = new StringBuilder(128);
jpql.append("SELECT d");
jpql.append(" FROM Demandeur d");
jpql.append(" WHERE UPPER(d.nom) LIKE :query");
jpql.append(" OR d.numeroOW LIKE :query");
Query demandeurQuery = em.createQuery(jpql.toString());
demandeurQuery.setParameter("query", "%" + query.toUpperCase() + "%");
return (List<Demandeur>) demandeurQuery.getResultList();
}
If the user selects a suggestion, it would set the itemValue to the name of the selected suggestion, but display a concatenated string of 2 values from the Demandeur object.
The suggestions do appear and I can select them, but unfortunely, I get this error when submitting the page:
Exception message: /page/utilisateur.xhtml at line 27 and column 50 itemLabel="#{demandeurItem ne null ? demandeurItem.numeroOW.toString().concat(' - ').concat(demandeurItem.nom) : ''}": Property 'numeroOW' not found on type java.lang.String
My understanding is that the var attribute of the autoComplete component is an iterator on the suggestions, so in my case of type Demandeur, not String.
Any help would be appreciated!
Thanks
I am using primefaces 3.5.11, MyFaces implementation for JSF on Websphere 8.5.5.0
Edit:
Here is the converter I tried with
@FacesConverter(value = "demandeurUIConverter")
public class DemandeurConverter implements Serializable, Converter {
private static final long serialVersionUID = 1L;
@Override
public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String value) throws ConverterException {
if (value == null || value.length() == 0) {
return null;
}
ConverterCtrl cc = EJB.lookup(ConverterCtrl.class);
Demandeur d = cc.getDemandeurFromCle(value);
if (d == null) {
d = new Demandeur();
d.setNom(value);
d.setNumeroOW(value);
}
return d;
}
@Override
public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object value) throws ConverterException {
if (value == null) {
return "";
}
Demandeur demandeur = (Demandeur) value;
return demandeur.getNom();
}
}
Solution 1:[1]
This will work if you make a converter for Demandeur
.
Solution 2:[2]
Actually, the problem seems to be this part
itemValue="#{demandeurItem.nom}"
if the itemValue is the name of your item, then the converter will try to convert from a String and not from the object. So your converter method below will receive "value" = string, not a Demandeur as you may expect.
public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object value) throws ConverterException
I've tried this code below and I think it's what you need. I am using primefaces 4.0 on tomee 1.6.0.
the converter
import java.io.Serializable;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
@ManagedBean
@RequestScoped
public class DemandeurConverter implements Converter, Serializable {
private static final long serialVersionUID = 1L;
@EJB
Demandeurs ejb;
@Override
public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String value) throws ConverterException {
if (value == null || value.length() == 0) {
return null;
} else {
return ejb.getData().get(Long.parseLong(value));
}
}
@Override
public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object value) throws ConverterException {
System.out.println(value.getClass());
if (value == null) {
return null;
} else {
return ((Demandeur) value).getId().toString();
}
}
}
the managed bean
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
@ManagedBean
@ViewScoped
public class UtilisateurDemandeurCtrl implements Serializable {
private static final long serialVersionUID = -3027573774106311465L;
@EJB
private Demandeurs ejb;
private Demandeur critereRechercheDemandeur;
public List<Demandeur> completeDemandeur(String query) {
List<Demandeur> l = new ArrayList<Demandeur>();
for(Entry<Long, Demandeur> entryset:ejb.getData().entrySet()){
if (entryset.getValue().getNom().contains(query)){
l.add(entryset.getValue());
}
}
return l;
}
public Demandeur getCritereRechercheDemandeur() {
return critereRechercheDemandeur;
}
public void setCritereRechercheDemandeur(Demandeur critereRechercheDemandeur) {
this.critereRechercheDemandeur = critereRechercheDemandeur;
}
}
the EJB
import java.util.HashMap;
import java.util.Map;
import javax.ejb.Singleton;
@Singleton
public class Demandeurs {
private static final Map<Long,Demandeur> data = new HashMap<Long,Demandeur>(){
private static final long serialVersionUID = -4394378761837292672L;
{
put(1L,new Demandeur(1L,"ooooooooooone",111));
put(2L,new Demandeur(2L,"ttttttttttttwo",222));
}
};
public static Map<Long, Demandeur> getData() {
return data;
}
}
the entity bean
import java.io.Serializable;
public class Demandeur implements Serializable{
private static final long serialVersionUID = 4023658749746098762L;
private Long id;
private String nom;
private Integer numeroOW;
public Demandeur() {}
public Demandeur(Long id, String nom, Integer numeroOW) {
super();
this.id = id;
this.nom = nom;
this.numeroOW = numeroOW;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getNom() {
return nom;
}
public void setNom(String nom) {
this.nom = nom;
}
public Integer getNumeroOW() {
return numeroOW;
}
public void setNumeroOW(Integer numeroOW) {
this.numeroOW = numeroOW;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((nom == null) ? 0 : nom.hashCode());
result = prime * result + ((numeroOW == null) ? 0 : numeroOW.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Demandeur other = (Demandeur) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (nom == null) {
if (other.nom != null)
return false;
} else if (!nom.equals(other.nom))
return false;
if (numeroOW == null) {
if (other.numeroOW != null)
return false;
} else if (!numeroOW.equals(other.numeroOW))
return false;
return true;
}
}
and the xhtml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<h:head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Test</title>
<h:outputScript library="js" name="common.js" />
</h:head>
<h:body>
<h:form id="somePrefix">
<p:autoComplete
id="it_demandeur"
value="#{utilisateurDemandeurCtrl.critereRechercheDemandeur}"
var="demandeurItem"
converter="#{demandeurConverter}"
itemLabel="#{demandeurItem ne null ? demandeurItem.numeroOW.toString().concat(' - ').concat(demandeurItem.nom) : ''}"
itemValue="#{demandeurItem}"
forceSelection="true"
completeMethod="#{utilisateurDemandeurCtrl.completeDemandeur}">
<p:column>
#{demandeurItem.numeroOW} - #{demandeurItem.nom}
</p:column>
</p:autoComplete>
</h:form>
</h:body>
</html>
Solution 3:[3]
I am having the same exact issue.
This gives me an error: itemLabel="#{user.fullName}": Property 'fullName' not found on type java.lang.String
The issue comes up only when you update the autocomplete component after a submit (to show validation messages). If you remove "update" attribute from a commandButton, it works just fine.
<p:panel id="resourceConfigNewFormPanel">
<p:autoComplete id="newUsername" value="#{userResourceConfigurationListBean.resourceConfig.username}" completeMethod="#{userResourceConfigurationListBean.autocompleteUser}" var="user" itemLabel="#{user.fullName}" itemValue="#{user.userName}" forceSelection="true" required="true">
</p:autoComplete>
<p:commandButton value="..." action="..." update="resourceConfigNewForm" oncomplete="if (args && !args.validationFailed) resourceConfigNewDlg.hide()" />
And this works with and without update:
<p:autoComplete id="newUsername" value="#{userResourceConfigurationListBean.resourceConfig.user}" completeMethod="#{userResourceConfigurationListBean.autocompleteUser}" var="user" itemLabel="#{user.fullName}" itemValue="#{user}" converter="#{userNameToUserConverter}" forceSelection="true" required="true">
</p:autoComplete>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | rdcrng |
Solution 2 | |
Solution 3 | user2027349 |