2010-09-01 19 views
19

Come posso scrivere un convertitore personalizzato quando si lavora con componenti PrimeFaces che utilizzano un elenco di POJO? Il mio problema particolare è con <p:pickList>Come scrivere un convertitore personalizzato per <p:pickList>

<p:pickList converter="????" value="#{bean.projects}" var="project" 
          itemLabel="#{project.name}" itemValue="#{project}"> 

Senza un convertitore ottengo java.lang.ClassCastException perché JSF imposta i valori presentati unitamente non convertiti java.lang.String valori presentati.

risposta

18

Dopo una ricerca su come scrivere un convertitore personalizzato, ecco la soluzione.
1. Creare una classe Java che implementano javax.faces.convert.Converter;

public class ProjectConverter implements Converter{ 

    @EJB 
    DocumentSBean sBean; 

    public ProjectConverter(){ 
    } 

    public Object getAsObject(FacesContext context, UIComponent component, String value){ 
    return sBean.getProjectById(value); 
    //If u look below, I convert the object into a unique string, which is its id. 
    //Therefore, I just need to write a method that query the object back from the 
    //database if given a id. getProjectById, is a method inside my Session Bean that 
    //does what I just described 
    } 

    public String getAsString(FacesContext context, UIComponent component, Object value)  
    { 
    return ((Project) value).getId().toString(); //--> convert to a unique string. 
    } 
} 

2. Registrare il convertitore personalizzato in faces-config.xml

<converter> 
    <converter-id>projectConverter</converter-id> 
    <converter-class>org.xdrawing.converter.ProjectConverter</converter-class> 
</converter> 

3. Così ora all'interno componente primefaces, u solo fare converter="projectConverter". Si noti che projectConverter è il <convert-id> appena creato. Quindi per risolvere il mio problema, faccio questo:

<p:pickList converter="projectConverter" value="#{bean.projects}" var="project" 
          itemLabel="#{project.name}" itemValue="#{project}"> 
2

c'è un modo per implementarlo senza 2 riscontri di database?

Voglio dire, quando si ha

#{bean.projects} 

questo è un successo di database.

e quando il convertitore mette

sBean.getProjectById(value); 

è un colpo di database non necessaria, dal momento che hanno già bean.projects id e valore degli oggetti

+0

Domanda interessante. Sfortunatamente onestamente non lo so. Penso che dovresti fare una domanda e vedere cosa dice la community. Se lo fai in un post, per favore dammi il link. Voglio sapere anche questo. –

37

E 'possibile, whithout altro accesso al database, ma i don' so il modo migliore Io uso un convertitore molto specifico, funziona solo per picklist. Prova questo:

@FacesConverter(value = "primeFacesPickListConverter")public class PrimeFacesPickListConverter implements Converter { 
@Override 
public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2) { 
    Object ret = null; 
    if (arg1 instanceof PickList) { 
     Object dualList = ((PickList) arg1).getValue(); 
     DualListModel dl = (DualListModel) dualList; 
     for (Object o : dl.getSource()) { 
      String id = "" + ((Project) o).getId(); 
      if (arg2.equals(id)) { 
       ret = o; 
       break; 
      } 
     } 
     if (ret == null) 
      for (Object o : dl.getTarget()) { 
       String id = "" + ((Project) o).getId(); 
       if (arg2.equals(id)) { 
        ret = o; 
        break; 
       } 
      } 
    } 
    return ret; 
} 

@Override 
public String getAsString(FacesContext arg0, UIComponent arg1, Object arg2) { 
    String str = ""; 
    if (arg2 instanceof Project) { 
     str = "" + ((Project) arg2).getId(); 
    } 
    return str; 
} 

e Picklist:

<p:pickList converter="primeFacesPickListConverter" value="#{bean.projects}" var="project" 
         itemLabel="#{project.name}" itemValue="#{project}"> 

di lavoro per me, i miglioramenti è necessario.

+4

Questa è la soluzione migliore. Interrogare il database in un convertitore è la cosa peggiore che si possa fare, aggiungere 1000 oggetti all'accelist, con almeno un'entità annidata, e vedere cosa succede;) – Stefan

+0

+1 perché è meglio della risposta accettata. esattamente quello che stavo cercando. – Anas

+0

a proposito, puoi dirmi cosa è 'Project' nel tuo codice? è la doppia lista in bean? o 'var'? – Anas

5

Sì, è possibile scrivere un convertitore che serializza/deserializza gli oggetti nella picklist in questo modo:

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

    private static final Logger log = Logger.getLogger(PositionMetricConverter.class.getName()); 

    @Override 
    public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String value) { 
     try { 
      byte[] data = Base64.decodeBase64(value); 
      ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data)); 
      Object o = ois.readObject(); 
      ois.close(); 
      return o; 
     } catch (Exception e) { 
      log.log(Level.SEVERE, "Unable to decode PositionMetric!", e); 
      return null; 
     } 
    } 

    @Override 
    public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object value) { 
     try { 
      ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
      ObjectOutputStream oos = new ObjectOutputStream(baos); 
      oos.writeObject(value); 
      oos.close(); 

      return Base64.encodeBase64String(baos.toByteArray()); 
     } catch (IOException e) { 
      log.log(Level.SEVERE, "Unable to encode PositionMetric!", e); 
      return ""; 
     } 
    } 

} 

quindi applicare questo convertitore sul tuo elenco di selezione in questo modo:

<p:pickList converter="PositionMetricConverter" value="#{bean.positionMetrics}" 
    var="positionMetric" itemLabel="#{positionMetric.name}" itemValue="#{positionMetric}"/> 

e fare sicuro che i tuoi oggetti siano serializzabili.

3

Questo problema non è relativo alle primefaces, ma solo al JSF generale correlato.

Perché si dovrebbe colpire nuovamente il database? Il tuo bean contiene già l'elenco di oggetti che vuoi mostrare in un componente o è richiesto per ambito?

  • Crea una Superclasse per il tuo Pojo Hibernate contenente un campo ID. Se non vuoi creare una super classe usa semplicemente la classe pojo, ma hai bisogno di più classi di convertitori.
  • Con tale Superclasse è possibile creare un convertitore generico per tutte le classi Pojo contenente un elenco di Pojo passati nel costruttore.
  • Aggiungere il convertitore come proprietà nel bean di sessione e utilizzare quel convertitore nel componente JSF.

Se si accede all'elenco finale tramite una proprietà get nel bean o uno nidificato nel convertitore è di vostra scelta.

public class SuperPojo 
{ 
    protected Integer id; 
    //constructor & getter 
} 

public class PojoTest extends SuperPojo 
{ 
    private String label = null;  
    //constructor & getter 
} 

public class SuperPojoConverter<T extends SuperPojo> implements Converter 
{ 
    private Collection<T> superPojos; 

    public IdEntityConverter(Collection<T> superPojos) 
    { 
     this.superPojos = superPojos; 
    } 

    @Override 
    public Object getAsObject(FacesContext context, UIComponent component, String value) 
    { 
     //catch exceptions and empty or null value! 
     final int intValue = Integer.parseInt(value); 
     for(SuperPojo superPojo : this.superPojos) 
      if(superPojo.getId().intValue() == intValue) 
       return superPojo; 

     return null; 
    } 

    @Override 
    public String getAsString(FacesContext context, UIComponent component, Object value) 
    { 
     //catch null and instanceof 
     return String.valueOf(((SuperPojo)value).getId().intValue()); 
    } 

    public Collection<T> getSuperPojos() 
    { 
     return this.superPojos; 
    } 
} 

public class Bean 
{ 
    private SuperPojoConverter<PojoTest> pojoTestConverter = null; 

    public Bean() 
    { 
     final List<PojoTest> pojoTests = //get list from hibernate 
     this.pojoTestConverter = new SuperPojoConverter<PojoTest>(pojoTests); 
    } 

    public SuperPojoConverter<PojoTest> getPojoTestConverter() 
    { 
     return this.pojoTestConverter; 
    } 
} 


<h:selectOneMenu value="#{selPojoTest}" converter="#{bean.getPojoTestConverter}"> 
    <f:selectItems value="#{bean.getPojoTestConverter.getSuperPojos}" var="varPojoTest" itemLabel="#{varPojoTest.label}" itemValue="#{varPojoTest}"/> 
</h:selectOneMenu> 
1

Sì, è possibile:

public class DocumentSBean sBean implements Serializable{ 

private List<Document> projects; 
// projects methods... 
// ... 

public Converter getDocumentConverter(){ 
return docConverter; 
} 

private Converter docConverter = new Converter() { 

     @Override 
     public Object getAsObject(FacesContext context, UIComponent component, String value) { 
      return projects.stream().filter(p -> p.getName().equals(value)).findFirst().orElse(null); 
     } 

     @Override 
     public String getAsString(FacesContext context, UIComponent component, Object value) { 
      return (value != null) 
        ? ((Document) value).toString() 
        : null; 
     } 
    }; 
} 
<p:pickList converter="#{sBean.documentConverter}" value="#... 
Problemi correlati