venerdì 18 gennaio 2013

Primefaces 3.4.2 problemi con p:selectOneMenu

Sono incappato in un problema con la versione 3.4.2 del framework, al momento l'ultima release stabile.
Il problema si verifica quando , in una coppia di combo legate via ajax si seleziona la prima combo e parte l'evento di caricamento sulla seconda.
Se la selezione avviene dopo aver digitato un valore sulla combo l'evento onchange javascript non scatta, quindi il caricamento delle combo collegata non avviene.
Il bug è descritto nel forum di primefaces (http://forum.primefaces.org/viewtopic.php?f=3&t=28035&p=88388#p88388 ) e si risolve sostituendo alla versione 3.4.2 la versione 3.4.5 al momento in versione SNAPSHOT.
Per verificare il problema ho preso l'esempio dallo showcase di primefaces. Colgo l'occasione per postare il codice del progetto.

JAR

Sono sufficienti 2 soli jar
  • L'ultima versione di Mojarra (http://javaserverfaces.java.net/download.html), nel mio caso la 2.1.17;
  • L'ultima versione di PrimeFaces (http://www.primefaces.org/downloads.html), nel mio caso la 3.4.2
WEB.XML

Qui bisogna configurare la servlet di JSF + gli url pattern necessari. E' opzionale (ma è meglio farlo) inserire anche il parametro relativo all'environment (production,test, etc.) .
Di seguito l'estratto del web.xml


<servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.jsf</url-pattern>
  </servlet-mapping>
  <context-param>
    <param-name>javax.faces.PROJECT_STAGE</param-name>
    <param-value>Production</param-value>
  </context-param>
 
Con JSF 2.0 èopzionale l'utilizzo del faces-config.xml, in quanto si possono usare le annotation sui bean.

Per il codice ho preso tutto dall'esempio di primefaces che si trova qui.

MANAGED BEAN


package it.showcase;
import java.io.Serializable;  
import java.util.HashMap;  
import java.util.Map;  
import javax.faces.application.FacesMessage;  
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;  
@ManagedBean(name="pprBean")
@ViewScoped
public class PPRBean implements Serializable {  
 private static final long serialVersionUID = 1L;

 private String city;  
  
    private String suburb;  
      
    private Map cities = new HashMap();  
  
    private Map> suburbsData = new HashMap>();  
      
    private Map suburbs = new HashMap();  
  
    public PPRBean() {  
        cities.put("Istanbul", "Istanbul");  
        cities.put("Ankara", "Ankara");  
        cities.put("Izmir", "Izmir");  
          
        Map suburbsIstanbul = new HashMap();  
        suburbsIstanbul.put("Kadikoy", "Kadikoy");  
        suburbsIstanbul.put("Levent", "Levent");  
        suburbsIstanbul.put("Cengelkoy", "Cengelkoy");  
          
        Map suburbsAnkara = new HashMap();  
        suburbsAnkara.put("Kecioren", "Kecioren");  
        suburbsAnkara.put("Cankaya", "Cankaya");  
        suburbsAnkara.put("Yenimahalle", "Yenimahalle");  
          
        Map suburbsIzmir = new HashMap();  
        suburbsIzmir.put("Cesme", "Cesme");  
        suburbsIzmir.put("Gumuldur", "Gumuldur");  
        suburbsIzmir.put("Foca", "Foca");  
          
        suburbsData.put("Istanbul", suburbsIstanbul);  
        suburbsData.put("Ankara", suburbsAnkara);  
        suburbsData.put("Izmir", suburbsIzmir);  
    }  
      
    public String getCity() {  
        return city;  
    }  
  
    public void setCity(String city) {  
        this.city = city;  
    }  
  
    public String getSuburb() {  
        return suburb;  
    }  
  
    public void setSuburb(String suburb) {  
        this.suburb = suburb;  
    }  
  
    public Map getCities() {  
        return cities;  
    }  
  
    public void setCities(Map cities) {  
        this.cities = cities;  
    }  
      
    public Map> getSuburbsData() {  
        return suburbsData;  
    }  
  
    public void setSuburbsData(Map> suburbsData) {  
        this.suburbsData = suburbsData;  
    }  
      
    public Map getSuburbs() {  
        return suburbs;  
    }  
  
    public void setSuburbs(Map suburbs) {  
        this.suburbs = suburbs;  
    }  
      
    public void handleCityChange() {  
        if(city !=null && !city.equals(""))  
            suburbs = suburbsData.get(city);  
        else  
            suburbs = new HashMap();  
    }  
  
    public void displayLocation() {  
        FacesMessage msg = new FacesMessage("Selected", "City:" + city + ", Suburb: " + suburb);  
  
        FacesContext.getCurrentInstance().addMessage(null, msg);  
    }  
}  


La pagina xhtml è la seguente:

<?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:h="http://java.sun.com/jsf/html"  
    xmlns:f="http://java.sun.com/jsf/core"  
    xmlns:p="http://primefaces.org/ui"
    xmlns:ui="http://java.sun.com/jsf/facelets">  
   <h:head>
   </h:head>
<h:body> 
    <h:form id="form">  
      
        <p:growl id="msgs" showDetail="true"/>  
                      
        <p:panel header="Double Combo" style="margin-bottom:10px;">  
            <h:panelGrid columns="2" cellpadding="5">  
                <p:selectOneMenu id="city" value="#{pprBean.city}">  
                    <f:selectItem itemLabel="Select City" itemValue="" />  
                    <f:selectItems value="#{pprBean.cities}" />  
                    <p:ajax update="suburbs"  
                            listener="#{pprBean.handleCityChange}" />  
                </p:selectOneMenu>  
      
                <p:selectOneMenu id="suburbs" value="#{pprBean.suburb}">  
                    <f:selectItem itemLabel="Select Suburb" itemValue="" />  
                    <f:selectItems value="#{pprBean.suburbs}" />  
                </p:selectOneMenu>  
                
                <h:selectOneMenu id="city2" value="#{pprBean.city}">  
                    <f:selectItem itemLabel="Select City" itemValue="" />  
                    <f:selectItems value="#{pprBean.cities}" />  
                    <p:ajax update="suburbs2"  
                            listener="#{pprBean.handleCityChange}" />  
                </h:selectOneMenu>  
      
                <p:selectOneMenu id="suburbs2" value="#{pprBean.suburb}">  
                    <f:selectItem itemLabel="Select Suburb" itemValue="" />  
                    <f:selectItems value="#{pprBean.suburbs}" />  
                </p:selectOneMenu>  
                
                
            </h:panelGrid>  
      
            <p:separator />  
              
            <p:commandButton value="Submit" update="msgs" actionListener="#{pprBean.displayLocation}" id="btnSubmit"/>  
      
        </p:panel>  
      
    </h:form>  
</h:body>
</html>


Si noti che ho inserito oltre alle 2 combo base dell'esempio un'altra coppia sostituendo il componente jsf standard (h:selectOneMenu invece di p:selectOneMenu).
In questo modo si può verificare l'errato comportamento del p:selectOneMenu.
Se si accede alla pagina, si scrive ad esempio la lettera I nella combo e poi si seleziona Istanbul la combo"Suburb" non viene popolata.
Se si effettua lo stesso test nella coppia di combo sottostanti invece funziona correttamente.
Inserendo la versione 3.5 SNAPSHOT di PrimeFaces tutto funziona correttamente.

Nessun commento:

Posta un commento