2011-07-30 10 views
9

Sono di fronte al seguente problema: in una pagina, elenco tutti gli utenti della mia applicazione e ho un pulsante "modifica" per ciascuno, che è un collegamento "GET" con ?id=<userid>.
jsf viewparam perso dopo l'errore di convalida

La pagina di modifica ha un valore <f:viewParam name="id" value="#{editUserBean.id}"/> nei metadati.
Se ho apportato qualche errore di input e invio (utilizzo la convalida CDI Weld Bean), la pagina viene nuovamente visualizzata, ma ho perso lo ?id=... nell'URL e quindi ho perso l'ID utente dell'utente che sto modificando.

Ho esaminato un problema simile descritto in JSF validation error, lost value, ma la soluzione con inputhidden (o peggio, con tomahawk, che sembra eccessivo) richiede molto codice uggly.

Ho provato ad aggiungere una "Conversazione" con CDI, e funziona, ma mi sembra troppo eccessivo per me ancora.

Esiste una soluzione semplice in JSF per conservare i parametri di visualizzazione in caso di errori di convalida?

[Il mio ambiente: Tomcat7 + MyFaces 2.1.0 + Hibernate Validator 4.2.0 + CDI (Weld) 1.1.2]

+3

stai usando 'includeViewParams = true' nella vostra azione presentare? – bluefoot

+0

No, ma il mio gestore di azioni non viene chiamato: l'invio non riesce prima nella fase di convalida. – DenisGL

risposta

7

caso interessante. Per tutti, il seguente codice minimo riproduce questo:

facelet:

<html xmlns="http://www.w3.org/1999/xhtml" 
    xmlns:h="http://java.sun.com/jsf/html" 
    xmlns:f="http://java.sun.com/jsf/core" 
> 

    <f:metadata> 
     <f:viewParam id="id" name="id" value="#{viewParamBean.id}"/> 
    </f:metadata> 

    <h:body> 

     <h:messages /> 

     #{viewParamBean.id} <br/> 

     <h:form> 
      <h:inputText value="#{viewParamBean.text}" > 
       <f:validateLength minimum="2"/> 
      </h:inputText> 

      <h:commandButton value="test" action="#{viewParamBean.actionMethod}"/> 
     </h:form> 

    </h:body> 
</html> 

Bean:

@ManagedBean 
@RequestScoped 
public class ViewParamBean { 

    private long id;  
    private String text; 

    public void actionMethod() { 

    } 

    public long getId() { 
     return id; 
    } 

    public void setId(long id) { 
     this.id = id; 
    } 

    public String getText() { 
     return text; 
    } 

    public void setText(String text) { 
     this.text = text; 
    }  
} 

Se si chiama il facelet con viewparam.xhtml?id=12 viene visualizzata la 12 sullo schermo. Se quindi inserisci qualcosa di valido, ad es. aaaaa, l'ID sparirà dall'URL, ma continua a essere visualizzato sullo schermo (in possesso della natura di stato dei componenti dell'interfaccia utente).

Tuttavia ... come OP menzionato, non appena si verifica un errore di convalida (ad esempio, immettendo a), l'ID verrà definitivamente perso. L'immissione di input validi in seguito non la riporterà indietro. Sembra quasi un bug, ma ho provato sia Mojarra 2.1 che Myfaces 2.1 ed entrambi hanno lo stesso comportamento.

Aggiornamento:

Dopo un po 'di ispezione, il problema sembra essere in questo metodo di `UIViewParameter' (Mojarra):

public void encodeAll(FacesContext context) throws IOException { 
    if (context == null) { 
     throw new NullPointerException(); 
    } 

    // if there is a value expression, update view parameter w/ latest value after render 
    // QUESTION is it okay that a null string value may be suppressing the view parameter value? 
    // ANSWER: I'm not sure. 
    setSubmittedValue(getStringValue(context)); 
} 

E poi più specificamente questo metodo:

public String getStringValue(FacesContext context) { 
    String result = null; 
    if (hasValueExpression()) { 
     result = getStringValueFromModel(context); 
    } else { 
     result = (null != rawValue) ? rawValue : (String) getValue(); 
    } 
    return result; 
} 

Perché hasValueExpression() è vero, proverà a ottenere il valore dal modello (il bean di supporto). Ma dal momento che questo bean è stato richiesto con scope, non avrà alcun valore per questa richiesta, poiché la validazione è appena fallita e quindi nessun valore è mai stato impostato.In effetti, il valore di stato di UIViewParameter viene sovrascritto da qualunque cosa il backing bean ritorni come predefinito (tipicamente nullo, ma ovviamente dipende dal bean).

Una soluzione è quella di rendere il bean @ViewScoped, che spesso è un ambito meglio comunque (suppongo si utilizza il parametro per ottenere un utente da un servizio, ed è forse inutile farlo più e più volte ad ogni postback) .

Un'altra alternativa è creare la propria versione di UIViewParameter che non tenta di ottenere il valore dal modello se la convalida non è riuscita (come in pratica tutti gli altri componenti UIInput).

+1

Per chi è interessato; Creo un problema a questo indirizzo: http://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-1029 per favore vota se desideri che venga corretto. –

+0

Ho notato un altro strano comportamento risultante da questo problema (con Mojarra (2.1.2), ma non con MyFaces (2.1.0)): se il parametro view è impostato come 'required =" true "', una richiesta Ajax fallirà nella fase di validazione. – DenisGL

+3

FYI: il 'UIViewParameter' rattoppato è disponibile come' 'di OmniFaces. Vedi anche http://showcase.omnifaces.org/components/viewParam – BalusC

1

Tu in realtà non perde il parametro vista. f: viewParam ha lo stato, quindi anche se non è nell'URL, è ancora lì. Basta inserire un punto di interruzione o system.out nel setter per visualizzare il parametro.

(se google su viewParam stateful stateless troverete qualche info in più)

+0

Grazie mille, ho trovato un articolo interessante qui [link] (http://jdevelopment.nl/stateless-stateful-jsf-view-parameters/). Tuttavia, ho ancora il problema: se si invia il mio modulo senza errori, viene chiamato il setter associato al parametro view, ma se faccio un errore nel mio input e invio nuovamente il valore è perso. – DenisGL

+1

UIViewParameter è in effetti stato come descritto in quell'articolo, ma a causa del codice problematico nel metodo 'encode' di quel componente, lo sovrascrive con il valore predefinito che si trova nel bean di supporto. Vedi la mia risposta aggiornata. –

+0

Ok, è normalmente di stato, ma a causa di un controllo del design il valore viene effettivamente perso ogni volta che ci sono errori di validazione. Credo di aver imparato qualcosa di nuovo! –

0

Ho lo stesso nella mia applicazione. Sono passato a @ViewAccessScoped che consente di implementare in modo più elegante.

0
<f:metadata> 
     <f:viewParam id="id" name="id" value="#{baen.id}"/> 
    </f:metadata> 

O quando la prima volta ottenere parametro da URL, salvarlo in mappa sessione e continuare l'uso di quella mappa, e dopo salvare/o aggiornare la mappa pulito modulo.

0

Questo è difficile, ma si può provare a ripristinare la visualizzazione dei parametri con la Storia API:

<?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://xmlns.jcp.org/jsf/html" 
     xmlns:f="http://xmlns.jcp.org/jsf/core" 
     xmlns:ui="http://xmlns.jcp.org/jsf/facelets"> 
    <f:metadata > 
     <f:viewParam name="param1" value="#{backingBean.viewParam1}" /> 
     <f:viewParam name="param2" value="#{backingBean.viewParam2}" /> 
     <f:viewAction action="#{view.viewMap.put('queryString', request.queryString)}" /> 
    </f:metadata> 
    <h:head> 
     <title>Facelet Title</title> 
    </h:head> 
    <h:body> 
     <ui:fragment rendered="#{facesContext.postback}" > 
      <script type="text/javascript"> 
       var url = '?#{view.viewMap.get('queryString')}'; 
       history.replaceState({}, document.title, url); 
      </script> 
     </ui:fragment> 
     <h:form> 
      <h:inputText id="name" value="#{backingBean.name}" /> 
      <h:message for="name" style="color: red" /> 
      <br /> 
      <h:commandButton value="go" action="#{backingBean.go}" /> 
     </h:form> 
     <h:messages globalOnly="true" /> 
    </h:body> 
</html> 
Problemi correlati