string - para - Reemplace las cadenas vacías o nulas con un valor especificado mientras se genera usando un convertidor JSF
primefaces selectonemenu converter example (1)
El siguiente es un convertidor pensado básicamente para recortar espacios blancos iniciales y finales y reemplazar más de un espacio entre palabras en una oración o texto con un solo espacio. El convertidor ahora está modificado para reemplazar cadenas vacías o null
con "No disponible" (puede ser localizado dinámicamente, si es necesario).
@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+", " ");
}
}
Como los convertidores no se invocan, cuando un valor de modelo es null
función de la pregunta anterior , com.sun.faces.renderkit.html_basic.TextRenderer
se ha extendido con la intención de invocar convertidores, cuando el valor de una propiedad en el modelo asociado es 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));
}
}
La siguiente prueba condicional se ha eliminado para que se pueda invocar el método getFormattedValue()
, incluso si se encuentra un valor null
.
Object currentObj = getValue(component);
if (currentObj != null) {
currentValue = getFormattedValue(context, component, currentObj);
}
Esto ha sido registrado en faces-config.xml
siguiente manera.
<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>
El convertidor StringTrimmer
todavía no se invoca ( getAsString()
), cuando un valor de propiedad en el modelo objetivo devuelve null
.
Poniendo una prueba condicional en EL como #{empty bean.value ? ''Not available'' : bean.value}
#{empty bean.value ? ''Not available'' : bean.value}
todas partes de la aplicación es locura. ¿Cualquier sugerencia?
Es Mojarra 2.2.12.
Actualización:
Los valores convertidos están disponibles, cuando una de las instrucciones de devolución dentro del método getFormattedValue()
devuelve una cadena vacía ""
, cuando currentValue
es null
, se modifica para devolver un valor convertido en una llamada a
javax.faces.convert.Converter.getAsString(FacesContext context, UIComponent component, Object value)
dentro de ese método getFormattedValue()
.
Por lo tanto, lo siguiente ,
if(currentValue == null) {
return "";
}
necesita ser reemplazado por,
if (currentValue == null) {
converter = Util.getConverterForClass("".getClass(), context);
return converter == null ? "" : converter.getAsString(context, component, currentValue);
}
(Necesita sugerencias).
En primer lugar, un Converter
nunca tiene la intención de imponer un "valor predeterminado".
Independientemente de la pregunta, haga lo que haga en getAsString()
, debe garantizar que la String
resultante se puede volver a convertir al Object
original cuando la vuelve a pasar a través de getAsObject()
. Tu convertidor no hace eso. Aunque es poco probable que lo use, técnicamente es necesario modificar el convertidor para convertir la cadena exacta "Not available"
en null
. En otras palabras, su convertidor debe diseñarse de tal manera que getAsObject()
y getAsString()
puedan pasar exitosamente el resultado de cada uno en un ciclo infinito y dar el mismo resultado cada vez.
En cuanto al requisito funcional concreto de imponer un valor predeterminado, en lugar de en un Converter
, debe hacerlo en el modelo o en la vista, dependiendo de dónde provenga el valor predeterminado real. En su caso específico, solo desea tener un texto / etiqueta de marcador de posición predeterminado en la interfaz de usuario cuando no existe tal valor. Esto pertenece a la vista.
Poniendo una prueba condicional en EL como
#{empty bean.value ? ''Not available'' : bean.value}
#{empty bean.value ? ''Not available'' : bean.value}
todas partes de la aplicación es locura.
Estoy completamente de acuerdo en que esto es una locura si ya estás a medio camino del desarrollo de la aplicación y tienes cientos de ellos en todos los lugares. Sin embargo, no es una locura si ya lo tomó en cuenta desde el principio. Si no lo hizo, entonces bien, aprenda la lección, muerda la bala y rutinariamente corrija el código en consecuencia. IDEs decentes tienen find y replace basado en expresiones regulares que podrían ayudar en esto. Todos han estado allí y han hecho ese tipo de locura, incluso yo mismo. Para reducir el texto estándar, envuélvalo en una función EL o archivo de etiqueta.
En cuanto al problema concreto de que el Converter
no se invoque cuando el valor del modelo es null
, lo que personalmente estoy totalmente de acuerdo con que es un comportamiento inesperado, esto siempre se informó como el número 630 de Mojarra . Esto se cierra más tarde como WONTFIX porque causó fallas en el caso de prueba y, después de todo, es mejor informarlo como un problema de especificación de JSF. Esto se hizo como JSF spec issue 475 (ya en JSF 1.2). Comprobé MyFaces 2.2.9 en esto y tampoco desencadenó el convertidor y por lo tanto expone el mismo problema de especificación.
El problema técnico, sin embargo, es comprensible. Un valor null
no tiene un getClass()
sensible, por lo que el convertidor no se puede buscar por clase de valor de esta manera. Funciona solo cuando el convertidor está registrado explícitamente en el componente.
<h:outputText value="#{bean.potentiallyNullValue}" converter="stringTrimmer" />
Debería funcionar cuando el valor es una cadena vacía ""
, que es relativamente trivial para implementar en getValue()
del renderizador personalizado que extiende el TextRenderer
de Mojarra.
@Override
protected Object getValue(UIComponent component) {
Object value = super.getValue(component);
return (value != null) ? value : "";
}
Sin embargo, cuando lo intenté aquí, todavía falló. Lo que resulta, el convertidor por clase se omite por completo cuando el valor es una instancia de String
. Todavía funciona solo cuando el convertidor está registrado explícitamente en el componente. Es muy probable que esto sea un descuido durante la implementación de la especificación 131 para JSF 1.2 (antes de esta versión, los convertidores para String.class
no eran compatibles en absoluto y este problema solo lo arreglaba para decodificar, no para codificar).
Esto puede anularse en el mismo renderizador personalizado (con la anulación getValue()
) con la anulación inferior de getFormattedValue()
por lo que el convertidor se busca explícitamente.
@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);
}
Tenga en cuenta que no necesita comprobar UIInput
ya que ha registrado explícitamente su procesador personalizado en javax.faces.Output
/ javax.faces.Text
familia / tipo de componente solamente (es decir, solo se ejecutará en los componentes <h:outputText>
)
No obstante, la mejor solución es crear una función EL o un archivo de etiqueta para esto.
<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}" />