Tengo problemas de comprensión de cómo utilizar la selección en JSF 2 con POJO/entidad de manera efectiva. Por ejemplo, estoy tratando de seleccionar una Warehouse entidad a través de la siguiente desplegable:

<h:selectOneMenu value="#{bean.selectedWarehouse}">
    <f:selectItem itemLabel="Choose one .." itemValue="#{null}" />
    <f:selectItems value="#{bean.availableWarehouses}" />
</h:selectOneMenu>

Y la de abajo bean gestionado:

@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()));
        }
    }

    //...
}

Aviso que yo uso todo el Warehouse entidad como el valor de SelectItem.

Cuando yo envíe el formulario, se produce el siguiente caras mensaje:

Error en la conversión de los valores de ajuste ‘[email protected]’ para ‘null Converter».

Tenía la esperanza de que JSF sólo pudo establecer la correcta Warehouse objeto de mi managed bean cuando me envuelve en un SelectItem. Envolviendo mi entidad dentro de la SelectItem estaba destinado a saltar la creación de un Converter de mi entidad.

¿Realmente tengo que usar un Converter cada vez que desee hacer uso de entidades en mi <h:selectOneMenu>? Debe de JSF ser posible extraer el elemento seleccionado de la lista de elementos disponibles. Si realmente tengo que usar un convertidor, ¿cuál es la manera práctica de hacerlo? Hasta ahora se me ocurrió esto:

  1. Crear un Converter aplicación para la entidad.
  2. Primordial de la getAsString(). Creo que no lo necesito ya que la propiedad de la etiqueta de la SelectItem será utilizado para mostrar el menú desplegable de la opción de la etiqueta.
  3. Primordial de la getAsObject(). Creo que esto va a ser utilizada para devolver la correcta SelectItem o entidad dependiendo del tipo de campo seleccionado se define en el managed bean.

La getAsObject() me confunde. ¿Cuál es la forma eficiente de hacer esto? Tener la cadena de valor, ¿cómo obtengo la entidad asociada objeto? Debo de consulta de la entidad objeto del servicio objeto basado en la cadena de valor y retorno de la entidad? O tal vez de alguna manera puedo acceder a la lista de las entidades que forman la selección de elementos, bucle de ellos para encontrar la correcta entidad, y el retorno de la entidad?

¿Cuál es la aproximación normal esto?

OriginalEl autor bertie | 2011-01-19

2 Comentarios

  1. 61

    Introducción

    JSF genera HTML. HTML es en Java términos, básicamente, una gran String. Para representar objetos Java en el HTML, tiene que ser convertida a String. También, cuando un formulario HTML es presentado, los valores enviados son tratados como String en la petición HTTP parámetros. Debajo de las mantas, JSF les extrae el HttpServletRequest#getParameter() que devuelve String.

    Para convertir entre un no-estándar de Java objeto (es decir, no un String, Number o Boolean para que EL no ha incorporado las conversiones, o Date para que JSF proporciona builtin <f:convertDateTime> etiqueta), usted realmente tiene que proporcionar una costumbre Conversor. El SelectItem no tiene ningún propósito especial. Es sólo un remanente de JSF 1.x cuando no sea posible suministrar por ejemplo, List<Warehouse> directamente a <f:selectItems>. También ha ningún tratamiento especial en cuanto a las etiquetas y a la conversión.

    getAsString()

    Que usted necesita para implementar getAsString() método de tal manera que el objeto de Java se ha representado en un único String representación que puede ser utilizado como parámetro de la petición HTTP. Normalmente, la técnica de IDENTIFICACIÓN (la base de datos de clave principal) se utiliza 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"));
        }
    }

    Nota que devolver una cadena vacía en el caso de un null/modelo vacío de valor es importante y requerida por el javadoc. Véase también El uso de un «por Favor, seleccione» f:selectItem con null/valor vacío en el interior de una p:selectOneMenu.

    getAsObject()

    Que usted necesita para implementar getAsObject() de tal manera que exactamente que String representación como devuelto por getAsString() puede ser convertida de nuevo a exactamente el mismo objeto Java especifica 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 pasar de nuevo el objeto devuelto como modelValue argumento de getAsString() y, a continuación, pasar a la obtenida cadena como submittedValue argumento de getAsObject() en un bucle infinito.

    Uso

    Finalmente acaba de anotar el Converter con @FacesConverter a gancho en el tipo de objeto en cuestión, JSF, a continuación, automáticamente toma el cuidado de conversión cuando Warehouse tipo nunca sale en la foto:

    @FacesConverter(forClass=Warehouse.class)

    Que fue la «canónica» JSF enfoque. Es después de todo no es muy eficaz como podría, de hecho, también tenemos agarró el elemento de la <f:selectItems>. Pero el punto más importante de una Converter es que devuelve un único String representación, de modo que el objeto de Java puede ser identificado por una simple String adecuado para pasar alrededor de HTTP y HTML.

    Conversor genérico basado en el método toString()

    JSF utilidad de la biblioteca OmniFaces tiene un SelectItemsConverter que funciona basado en toString() resultado de la entidad. De esta manera usted no necesita jugar con getAsObject() y caros de negocios/operaciones de base de datos más. Para algunos concretos ejemplos de uso, véase también el escaparate.

    Para usarlo, sólo tienes que registrar de la siguiente manera:

    <h:selectOneMenu ... converter="omnifaces.SelectItemsConverter">

    Y asegúrese de que el toString() de su Warehouse entidad devuelve un único representación de la entidad. Usted podría, por ejemplo, devolver directamente el ID:

    @Override
    public String toString() {
        return String.valueOf(id);
    }

    O algo más legible/reutilizable:

    @Override
    public String toString() {
        return "Warehouse[id=" + id + "]";
    }

    Véase también:


    No relacionados el problema, ya que JSF 2.0 no requiere explícitamente más para tener un List<SelectItem> como <f:selectItem> valor. Sólo un List<Warehouse> 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;
    muchas gracias por la respuesta detallada, solucionó mi problema ! El genérico JSF entidad converter es muy bonito !
    Eres bienvenido.
    Hola @BalusC, me han implementado esta solución y funciona, pero veo un problema. Si me intente mover el convertidor personalizado a un fichero separado bean, entonces cuando yo vuelva a cargar la página que dice «Error de Expresión: Objeto con nombre MyCustomCoverter no encontrado.». Por lo que este texto <f:converter converterId="MyCustomConverter"/> no puede encontrar el convertidor. Ahora, ¿cómo puedo hacer que funcione?
    Me faltaba la <f:converter converterId="ContactConverter" /> parte. Gracias @BalusC por señalarme la dirección correcta.

    OriginalEl autor BalusC

  2. 3

    Ejemplo de JSF conversor genérico 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 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>
    Esto ya es otra Q/A que se hace referencia en la respuesta original: stackoverflow.com/questions/17343032/…
    La diferencia es que no es necesario para el servicio de base de datos y llamadas.
    por favor explicar en más detalle EN tus respuesta
    Muchas gracias, esta solución funcionó fo mí.
    No olvides que la availableItems debe ser una lista en este caso. Se utilizó un HashMap y cambiado a la Lista.

    OriginalEl autor proxymo

Dejar respuesta

Please enter your comment!
Please enter your name here