19

Tengo una página de Facelets con <h:dataTable>. En cada fila hay un <h:selectBooleanCheckbox>. Si se selecciona la casilla de verificación, el objeto detrás de la fila correspondiente se debe establecer en el bean.Cómo utilizar <h: selectBooleanCheckbox> en <h:dataTable> o <ui:repeat> para seleccionar varios elementos?

  1. ¿Cómo puedo hacer esto?
  2. ¿Cómo obtener las filas seleccionadas o sus datos en un bean de respaldo?
  3. ¿O sería mejor hacerlo con <h:selectManyCheckbox>?
+0

Hola BalusC, esto no está trabajando en la paginación, cualquier indicio? –

Respuesta

48

Su mejor opción es unir el valor h:selectBooleanCheckbox con una propiedad Map<RowId, Boolean> donde RowId representa el tipo del identificador de fila. Tomemos un ejemplo que usted tiene un objeto Item cuya propiedad identificador id es una Long:

<h:dataTable value="#{bean.items}" var="item"> 
    <h:column> 
     <h:selectBooleanCheckbox value="#{bean.checked[item.id]}" /> 
    </h:column> 
    ... 
</h:dataTable> 
<h:commandButton value="submit" action="#{bean.submit}" /> 

la que se va a utilizar en combinación con:

public class Item { 
    private Long id; 
    // ... 
} 

y

public class Bean { 
    private Map<Long, Boolean> checked = new HashMap<Long, Boolean>(); 
    private List<Item> items; 

    public void submit() { 
     List<Item> checkedItems = checked.entrySet().stream() 
      .filter(Entry::getKey) 
      .map(Entry::getValue) 
      .collect(Collectors.toList()); 

     checked.clear(); // If necessary. 

     // Now do your thing with checkedItems. 
    } 

    // ... 
} 

Verá, el mapa se llena automáticamente con el id de todos los elementos de la tabla como clave y el valor de la casilla de verificación es automático. cally establecido como valor de mapa asociado con el elemento id como clave.

+0

¿No sería aún más fácil usar un 'Mapa' de tipo' 'por lo que solo necesita repetir el mapa en lugar de toda la lista? –

+0

@Markus: eso no es posible. La '' solo admite los valores 'boolean' o' Boolean'. – BalusC

+1

es la tabla de datos y el botón debe estar en la misma forma? puede publicar más detalles para el lado de la vista, o solo el botón debe estar en un formulario. –

1

Una forma de enviar un parámetro a través del <h:selectBooleanCheckbox> es enviarlo a través del título de la casilla de verificación. En el ValueChangeListener, puede obtenerlo del componente usando un getAttributes().get("title"). Esto ayuda en los casos en los que desea enviar un valor de ID como parámetro (a diferencia del índice de fila seleccionado).

+0

No estoy seguro de lo que quiere decir con "en comparación con el índice de fila seleccionado", pero si usted se está enfocando en mi respuesta, entonces tenga en cuenta que no estoy pasando índices de fila alrededor. Solo los ID de los elementos seleccionados. – BalusC

3

En el siguiente ejemplo estoy usando casillas de verificación para seleccionar dos o más productos para permitir al usuario comparar las especificaciones del producto en una nueva página web usando JSF 2.0.

Me tomó un buen tiempo encontrar el siguiente problema (totalmente obvio ahora por supuesto) así que pensé que merecía una mención para aquellos que intentaban usar la paginación con el código de BalusC (respuesta agradable BalusC, mucho más simple de lo que jamás imaginé sería).

Si está utilizando la paginación obtendrá nullpointers en la línea de:

si (checked.get (item.getId()))

-en código de BalusC anteriormente.

Esto se debe a que solo se agregan casillas de verificación al Mapa (doh; frente abofeteada). Para aquellos productos cuyas casillas de verificación nunca se muestran, debido a la paginación, esta línea generará un error de puntero nulo y se debe agregar una verificación para ignorar estos punteros nulos (suponiendo que todas las casillas de verificación estén desmarcadas en la carga de la página). Para que el usuario marque una casilla de verificación, entonces necesita mostrar la página de paginación para que todo funcione bien allí después.

Si algunas o todas las casillas de verificación deben marcarse en la carga de la primera página, esto no será de ninguna utilidad ...tendrá que agregarlos manualmente al Mapa para que se muestren correctamente en la carga de la página.

Nota: como utilizo un objeto JPA 'clase de entidad de la base de datos' también necesitaba usar @Transient para el ID en mi clase de entidad ProductTbl ya que todas las variables se consideran columnas en la base de datos por JPA, a menos que prefijado con @Transient. También estoy usando un segundo enlace para restablecer las casillas de verificación, que llama a clearSelections(), y mi 'submit' es un enlace que llama compareSelectedProducts() en lugar de un botón Enviar.

El código completo es el siguiente:

En la clase 'ProductTbl' Entidad derivado de la base de datos:

@Transient 
private Long id; 

public Long getId() 
{ 
    return id; 
} 

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

En el 'ProductSelection' bean de respaldo:

private Map<Long, Boolean> checked = new HashMap<Long, Boolean>(); 
private String errorMessage = ""; 
// List of all products. 
private List<ProductTbl> products; 
// List of products to compare. 
private List<ProductTbl> compareProducts; 

// Setters and getters for above... 

public String compareSelectedProducts() 
{ 
    // Reset selected products store. 
    compareProducts = new ArrayList(); 

    for (ProductTbl item: products) 
    { 
     // If there is a checkbox mapping for the current product then... 
     if(checked.get(item.getId()) != null) 
     { 
      // If checkbox is ticked then... 
      if (checked.get(item.getId())) 
      { 
       // Add product to list of products to be compared. 
       compareProducts.add(item); 
      } 
     } 
    } 

    if(compareProducts.isEmpty()) 
    { 
     // Error message that is displayed in the 'ErrorPage.xhtml' file. 
     errorMessage = "No Products selected to compare specifications. Select two or more products by ticking the check box in the second column 'Cmpr'"; 
     return "process_ErrorPage"; 
    } 

    // Rest of code to get product specification data ready to be displayed. 

    return "process_CompareSelected"; 
} 

public String clearSelections() 
{ 
    // Untick all checkbox selections. 
    checked.clear(); 

    return "process_MainSearchResult"; 
} 

En la página web de JSF 'MainSearchResult.xhtml':

<h:commandLink action="#{productSelection.compareSelectedProducts()}" value="Cmpr Specification Comparison Table" /> 
<h:commandLink action="#{productSelection.clearSelections()}" value="Clear Selected" /> 

<h:dataTable value="#{productSelection.products}" rows="#{productSelection.numberRowsToDisplay}" first="#{productSelection.rowStart}" var="item" headerClass="table-header" > 
    <h:column> 
     <f:facet name="header"> 
      <h:outputText style="font-size:12px" value="Cmpr" /> 
     </f:facet> 
     <div style="text-align:center;" > 
      <h:selectBooleanCheckbox value="#{productSelection.checked[item.id]}" /> 
     </div> 
    </h:column> 
</h:dataTable> 

En el archivo 'faces-config.xml':

<navigation-rule> 
    <navigation-case> 
     <from-outcome>process_MainSearchResult</from-outcome> 
     <to-view-id>/MainSearchResult.xhtml</to-view-id> 
    </navigation-case> 
</navigation-rule> 
<navigation-rule> 
    <navigation-case> 
     <from-outcome>process_CompareSelected</from-outcome> 
     <to-view-id>/CompareSelected.xhtml</to-view-id> 
    </navigation-case> 
</navigation-rule> 
<navigation-rule> 
    <navigation-case> 
     <from-outcome>process_ErrorPage</from-outcome> 
     <to-view-id>/ErrorPage.xhtml</to-view-id> 
    </navigation-case> 
</navigation-rule> 
Cuestiones relacionadas