jsf - para - primefaces selectonemenu converter example
Valor de configuración de error de conversión para ''convertidor nulo''-¿Por qué necesito un convertidor en JSF? (2)
Introducción
JSF genera HTML. HTML está en términos de Java básicamente una String
grande. Para representar objetos Java en HTML, deben convertirse a String
. Además, cuando se envía un formulario HTML, los valores enviados se tratan como String
en los parámetros de solicitud de HTTP. Bajo las cubiertas, JSF los extrae de HttpServletRequest#getParameter()
que devuelve String
.
Para convertir un objeto Java no estándar (es decir, no una String
, Number
o Boolean
para el cual EL tiene conversiones integradas, o Date
para la cual JSF proporciona la etiqueta <f:convertDateTime>
), realmente debe suministrar un Converter
personalizado. SelectItem
no tiene ningún propósito especial. Es solo un remanente de JSF 1.x cuando no fue posible suministrar, por ejemplo, List<Warehouse>
directamente a <f:selectItems>
. Tampoco tiene un tratamiento especial en cuanto a etiquetas y conversión.
getAsString ()
getAsString()
implementar el método getAsString()
de tal manera que el objeto Java deseado se represente en una representación de String
única que se pueda utilizar como parámetro de solicitud HTTP. Normalmente, la ID técnica (la clave principal de la base de datos) se usa aquí.
public String getAsString(FacesContext context, UIComponent component, Object modelValue) {
if (modelValue == null) {
return "";
}
if (modelValue instanceof Warehouse) {
return String.valueOf(((Warehouse) modelValue).getId());
} else {
throw new ConverterException(new FacesMessage(modelValue + " is not a valid Warehouse"));
}
}
Tenga en cuenta que devolver una cadena vacía en el caso de un valor de modelo nulo / vacío es significativo y requerido por el getAsString() . Consulte también Uso de "Seleccione" f: seleccione Elemento con valor nulo / vacío dentro de ap ap: selectOneMenu .
getAsObject ()
getAsObject()
implementar getAsObject()
de tal manera que exactamente String
representación de String
que devuelve getAsString()
se pueda convertir de nuevo exactamente en el mismo objeto Java especificado como modelValue
en getAsString()
.
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) {
if (submittedValue == null || submittedValue.isEmpty()) {
return null;
}
try {
return warehouseService.find(Long.valueOf(submittedValue));
} catch (NumberFormatException e) {
throw new ConverterException(new FacesMessage(submittedValue + " is not a valid Warehouse ID"), e);
}
}
En otras palabras, debe ser técnicamente capaz de devolver el objeto devuelto como argumento modelValue
de getAsString()
y luego volver a pasar la cadena obtenida como argumento de getAsObject()
en un bucle infinito.
Uso
Finalmente, solo anote el Converter
con @FacesConverter
para enganchar el tipo de objeto en cuestión, JSF se encargará automáticamente de la conversión cuando el tipo de Warehouse
entre en escena:
@FacesConverter(forClass=Warehouse.class)
Ese fue el enfoque "canónico" de JSF. Después de todo, no es muy efectivo ya que también podría haber agarrado el artículo de <f:selectItems>
. Pero el punto más importante de un Converter
es que devuelve una representación de String
única , de modo que el objeto Java podría identificarse mediante una String
simple adecuada para pasar en HTTP y HTML.
Convertidor genérico basado en toString ()
La biblioteca de utilidades JSF OmniFaces tiene un SelectItemsConverter
que funciona en función del resultado toString()
de la entidad. De esta forma ya no es necesario getAsObject()
con getAsObject()
y costosas operaciones de negocio / base de datos. Para algunos ejemplos de uso concreto, vea también el showcase .
Para usarlo, simplemente regístrelo de la siguiente manera:
<h:selectOneMenu ... converter="omnifaces.SelectItemsConverter">
Y asegúrese de que toString()
de su entidad de Warehouse
devuelve una representación única de la entidad. Por ejemplo, puede devolver directamente la ID:
@Override
public String toString() {
return String.valueOf(id);
}
O algo más legible / reutilizable:
@Override
public String toString() {
return "Warehouse[id=" + id + "]";
}
Ver también:
- Cómo llenar las opciones de h: selectOneMenu de la base de datos?
- Conversor de entidad JSF genérico : para que no tenga que escribir un convertidor para cada entidad.
- Usar enums en JSF selectitems - enums necesita ser tratado de manera un poco diferente
- Cómo inyectar @EJB, @PersistenceContext, @Inject, @Autowired, etc. en @FacesConverter?
No relacionado con el problema, ya que JSF 2.0 ya no se requiere explícitamente tener un valor List<SelectItem>
como <f:selectItem>
. Solo una List<Warehouse>
también sería suficiente.
<h:selectOneMenu value="#{bean.selectedWarehouse}">
<f:selectItem itemLabel="Choose one .." itemValue="#{null}" />
<f:selectItems value="#{bean.availableWarehouses}" var="warehouse"
itemLabel="#{warehouse.name}" itemValue="#{warehouse}" />
</h:selectOneMenu>
private Warehouse selectedWarehouse;
private List<Warehouse> availableWarehouses;
Tengo problemas para entender cómo usar la selección en JSF 2 con POJO / entidad de manera efectiva. Por ejemplo, estoy intentando seleccionar una entidad de Warehouse
través del menú desplegable a continuación:
<h:selectOneMenu value="#{bean.selectedWarehouse}">
<f:selectItem itemLabel="Choose one .." itemValue="#{null}" />
<f:selectItems value="#{bean.availableWarehouses}" />
</h:selectOneMenu>
Y el frijol administrado a continuación:
@Named
@ViewScoped
public class Bean {
private Warehouse selectedWarehouse;
private List<SelectItem> availableWarehouses;
// ...
@PostConstruct
public void init() {
// ...
availableWarehouses = new ArrayList<>();
for (Warehouse warehouse : warehouseService.listAll()) {
availableWarehouses.add(new SelectItem(warehouse, warehouse.getName()));
}
}
// ...
}
Tenga en cuenta que utilizo toda la entidad de Warehouse
como el valor de SelectItem
.
Cuando envío el formulario, esto falla con el siguiente mensaje de caras:
Error de conversión que establece el valor ''com.example.Warehouse@cafebabe'' para ''convertidor nulo''.
Esperaba que JSF pudiera simplemente establecer el objeto de Warehouse
correcto para mi bean administrado cuando lo SelectItem
en un SelectItem
. Envolver mi entidad dentro de SelectItem
estaba destinado a omitir la creación de un Converter
para mi entidad.
¿Realmente tengo que usar un Converter
siempre que quiera hacer uso de entidades en mi <h:selectOneMenu>
? Debería ser posible para JSF solo extraer el elemento seleccionado de la lista de elementos disponibles. Si realmente tengo que usar un convertidor, ¿cuál es la forma práctica de hacerlo? Hasta ahora llegué a esto:
- Crea una implementación de
Converter
para la entidad. -
getAsString()
elgetAsString()
. Creo que no es necesario, ya que la propiedad de etiqueta deSelectItem
se utilizará para mostrar la etiqueta de la opción desplegable. - Sobreescribiendo el
getAsObject()
. Creo que esto se utilizará para devolver elSelectItem
o entidad correcto, según el tipo del campo seleccionado definido en el bean gestionado.
El getAsObject()
me confunde. ¿Cuál es la manera eficiente de hacer esto? Teniendo el valor de la cadena, ¿cómo obtengo el objeto de la entidad asociada? ¿Debería consultar el objeto de entidad desde el objeto de servicio en función del valor de cadena y devolver la entidad? ¿O quizás de alguna manera puedo acceder a la lista de las entidades que forman los elementos de selección, recorrerlas en bucle para encontrar la entidad correcta y devolver la entidad?
¿Cuál es el enfoque normal de esto?
Ejemplo de convertidor genérico JSF con ABaseEntity e identificador:
ABaseEntity.java
public abstract class ABaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
public abstract Long getIdentifier();
}
SelectItemToEntityConverter.java
@FacesConverter(value = "SelectItemToEntityConverter")
public class SelectItemToEntityConverter implements Converter {
@Override
public Object getAsObject(FacesContext ctx, UIComponent comp, String value) {
Object o = null;
if (!(value == null || value.isEmpty())) {
o = this.getSelectedItemAsEntity(comp, value);
}
return o;
}
@Override
public String getAsString(FacesContext ctx, UIComponent comp, Object value) {
String s = "";
if (value != null) {
s = ((ABaseEntity) value).getIdentifier().toString();
}
return s;
}
private ABaseEntity getSelectedItemAsEntity(UIComponent comp, String value) {
ABaseEntity item = null;
List<ABaseEntity> selectItems = null;
for (UIComponent uic : comp.getChildren()) {
if (uic instanceof UISelectItems) {
Long itemId = Long.valueOf(value);
selectItems = (List<ABaseEntity>) ((UISelectItems) uic).getValue();
if (itemId != null && selectItems != null && !selectItems.isEmpty()) {
Predicate<ABaseEntity> predicate = i -> i.getIdentifier().equals(itemId);
item = selectItems.stream().filter(predicate).findFirst().orElse(null);
}
}
}
return item;
}
}
Y el uso:
<p:selectOneMenu id="somItems" value="#{exampleBean.selectedItem}" converter="SelectItemToEntityConverter">
<f:selectItem itemLabel="< select item >" itemValue="#{null}"/>
<f:selectItems value="#{exampleBean.availableItems}" var="item" itemLabel="${item.identifier}" itemValue="#{item}"/>
</p:selectOneMenu>