2016-02-17 33 views
12

Di seguito è riportato un convertitore inteso a tagliare gli spazi bianchi iniziali e finali e sostituire più spazi tra le parole in una frase o testo con un singolo spazio. Il convertitore è ora modificato per sostituire null o stringhe vuote con "Non disponibile" (potrebbe essere localizzato dinamicamente, se necessario).Sostituisce stringhe nulle o vuote con un valore specificato durante l'output utilizzando un convertitore JSF

@FacesConverter(forClass = String.class) 
public class StringTrimmer implements Converter { 

    @Override 
    public Object getAsObject(FacesContext context, UIComponent component, String value) { 
     return Boolean.TRUE.equals(component.getAttributes().get("skipConverter")) ? value : value == null ? null : value.trim().replaceAll("\\s+", " "); 
    } 

    @Override 
    public String getAsString(FacesContext context, UIComponent component, Object value) { 
     return Boolean.TRUE.equals(component.getAttributes().get("skipConverter")) ? value == null ? null : value.toString() : value == null || ((String) value).trim().length() == 0 ? "Not available" : ((String) value).trim().replaceAll("\\s+", " "); 
    } 
} 

Dal convertitori non vengono invocati, quando un valore modello è null sulla base di the previous question, com.sun.faces.renderkit.html_basic.TextRenderer è stata estesa con l'intenzione di convertitori invocano, quando un valore di proprietà nel modello associato è null.

public final class HtmlBasicRenderer extends TextRenderer { 

    @Override 
    public String getCurrentValue(FacesContext context, UIComponent component) { 

     if (component instanceof UIInput) { 
      Object submittedValue = ((UIInput) component).getSubmittedValue(); 

      if (submittedValue != null) { 
       return submittedValue.toString(); 
      } 
     } 

     return getFormattedValue(context, component, getValue(component)); 
    } 
} 

Il following test condizionale è stato rimosso in modo che il metodo getFormattedValue() può essere richiamato, anche se viene rilevato un valore null.

Object currentObj = getValue(component); 

if (currentObj != null) { 
    currentValue = getFormattedValue(context, component, currentObj); 
} 

Questo è stato registrato nel faces-config.xml come segue.

<render-kit> 
    <renderer> 
     <component-family>javax.faces.Output</component-family> 
     <renderer-type>javax.faces.Text</renderer-type> 
     <renderer-class>com.example.renderer.HtmlBasicRenderer</renderer-class> 
    </renderer> 
</render-kit> 

Il convertitore StringTrimmer non è ancora invocato (getAsString()), quando un valore di proprietà nel modello di destinazione restituisce null.

Inserire un test condizionale in EL come #{empty bean.value ? 'Not available' : bean.value} ovunque in tutta l'applicazione è follia. Qualche suggerimento?

È Mojarra 2.2.12.


Aggiornamento:

valori convertiti sono disponibili, quando una delle istruzioni return all'interno del metodo getFormattedValue() restituendo una stringa vuota "", quando currentValue è null, viene modificato per restituire un valore convertito nel call a

javax.​faces.​convert.​Converter.getAsString(FacesContext context, UIComponent component, Object value) 

all'interno di tale metodo getFormattedValue().

Così, il following,

if(currentValue == null) { 
    return ""; 
} 

ha bisogno di essere sostituito con,

if (currentValue == null) { 
    converter = Util.getConverterForClass("".getClass(), context); 
    return converter == null ? "" : converter.getAsString(context, component, currentValue); 
} 

(Bisogni suggerimenti).

+0

In quale componente stai provando questo (in quanto si crea come un generico 'forClass = String.class') Se solo nell'outputText, userei un componente SimpleCustom e cerco/sostituisco il d efault jsf o primeFaces uno con questo. – Kukeltje

+0

Fondamentalmente ''. È possibile creare componenti personalizzati per i campi opzionali selezionati, ma è necessario memorizzare con attenzione quali campi possono contenere valori nulli o vuoti. – Tiny

+0

Beh, il tipo di stato implicito (usando 'forClass = String.class') è per ** ogni ** stringa? Quindi non c'è bisogno di memorizzare le cose allora. Usa sempre il custom '' ;-) – Kukeltje

risposta

4

In primo luogo, un Converter non ha mai l'intenzione di applicare un "valore predefinito".

Indipendentemente della questione, qualunque cosa tu faccia nella getAsString(), si dovrebbe garantire che la risultante String può essere riconvertito a quello originale Object quando si passa indietro attraverso getAsObject(). Il tuo convertitore non lo fa. Anche se è improbabile che lo usi, tecnicamente il convertitore deve essere modificato per convertire la stringa esatta "Not available" di nuovo in null.In altre parole, il tuo convertitore deve essere progettato in modo tale che getAsObject() e getAsString() possano passare con successo i reciproci risultati in un loop infinito e restituire lo stesso risultato ogni volta.

Per quanto riguarda il requisito funzionale concreto di applicare un valore predefinito, anziché in un Converter, è necessario farlo nel modello o nella vista, a seconda di dove proviene il valore predefinito effettivo. Nel tuo caso specifico, vuoi semplicemente avere un testo/etichetta segnaposto predefinito nell'interfaccia utente quando non esiste tale valore. Questo appartiene alla vista.

Mettere un test condizionale in EL come #{empty bean.value ? 'Not available' : bean.value} ovunque in tutta l'applicazione è follia.

Sono pienamente d'accordo che questo è follia se sei già a metà strada come lo sviluppo di applicazioni e sono centinaia di loro su tutto posto. Tuttavia, non è follia se si è già tenuto conto sin dall'inizio. Se non l'hai fatto, allora bene, impara la lezione, morde il proiettile e sistematicamente aggiusti il ​​codice di conseguenza. Gli IDE decenti hanno una sostituzione basata su espressioni regolari & sostituibile che potrebbe aiutare in questo. Tutti sono stati lì e hanno fatto quel tipo di follia, anche me stesso. Per ridurre il boilerplate, avvolgilo in una funzione EL o tagfile.

Per quanto riguarda il problema concreto della Converter non essere invocato quando il valore di modello è null, che io personalmente concordo pienamente di essere un comportamento imprevisto, questo è stato mai segnalato come Mojarra issue 630. Successivamente viene chiuso come WONTFIX perché ha causato errori nel caso di test e, dopo tutto, è meglio che venga segnalato come un problema di specifica JSF. Questo è stato fatto come JSF spec issue 475 (già JSF 1.2). Ho controllato MyFaces 2.2.9 su questo e anche non attivare il convertitore e quindi espone lo stesso problema con le specifiche.

Il problema tecnico, tuttavia, è comprensibile. Un valore null non ha un valore ragionevole getClass(), quindi il convertitore non può essere cercato in base alla classe del valore in questo modo. Funziona solo quando il convertitore è esplicitamente registrato sul componente.

<h:outputText value="#{bean.potentiallyNullValue}" converter="stringTrimmer" /> 

Dovrebbe funzionare quando il valore è una stringa vuota "", che è relativamente banale da implementare in getValue() del renderer personalizzato estende Mojarra di TextRenderer.

@Override 
protected Object getValue(UIComponent component) { 
    Object value = super.getValue(component); 
    return (value != null) ? value : ""; 
} 

Tuttavia, quando ho provato che io stesso qui, ancora non è riuscito. Ciò che risulta, il convertitore per classe viene interamente ignorato quando il valore è un'istanza di String. Funziona ancora solo quando il convertitore è esplicitamente registrato sul componente. Questa è probabilmente una svista durante l'implementazione di spec issue 131 per JSF 1.2 (prima di questa versione, i convertitori per String.class non erano affatto supportati e questo problema è stato risolto solo per la decodifica, non per la codifica).

Questo può essere ignorato nello stesso renderizzatore personalizzato (con l'override getValue() precedente) con l'override inferiore a getFormattedValue() in cui il convertitore viene esplicitamente cercato.

@Override 
protected String getFormattedValue(FacesContext context, UIComponent component, Object currentValue) throws ConverterException { 
    Converter converter = ((UIOutput) component).getConverter(); 

    if (converter == null) { 
     converter = context.getApplication().createConverter(currentValue.getClass()); 
    } 

    return super.getFormattedValue(context, component, "".equals(currentValue) ? null : currentValue, converter); 
} 

Si noti che non è necessario per verificare la presenza di UIInput come hai esplicitamente registrato il renderer personalizzato su javax.faces.Output/javax.faces.Text componente della famiglia/tipo solo (vale a dire che verrà eseguito solo su <h:outputText> componenti).

Tuttavia, la soluzione migliore è ancora creare una funzione EL o un file di tag per questo.

<h:outputText value="#{empty bean.value ? 'Not available' : bean.value}" /> 
<h:outputText value="#{of:coalesce(bean.value, 'Not Available')}" /> 
<h:outputText value="#{of:coalesce(bean.value, i18n.na)}" /> 
<h:outputText value="#{your:value(bean.value)}" /> 
<your:text value="#{bean.value}" /> 
+0

Il valore di 'currentValue' all'interno di' getFormattedValue() 'può essere' null' in qualsiasi momento. Quindi, 'currentValue.getClass()' genererà un 'java.lang.NullPointerException' nel renderer stesso, se' currentValue' è 'null'. – Tiny

+0

Non se si esegue anche l'override di 'getValue()'. Forse non ero abbastanza chiaro, ho chiarito la risposta. – BalusC

+0

C'è un'altra conseguenza in un altro tipo di convertitore. Ho un convertitore 'BigDecimal' come in [domanda precedente] (http://stackoverflow.com/q/35347001/1391249). Se un valore di tipo 'BigDecimal' è' null' nel database, 'modelValue' recuperato in' getAsString() 'è una stringa vuota -' System.out.println ("modelValue:" + ((String) modelValue). isEmpty()); 'restituisce' true' in 'getAsString()'. Questo raggiunge 'throw new ConverterException (" Message ");' come specificato nella ladder 'if-else-if-else' all'interno del metodo' getAsString() '. ('modelValue' dovrebbe essere' null' lì, se è 'null' nel DB) vedo – Tiny