¿Cómo puedo pasar la fila seleccionada a commandLink dentro de dataTable o ui: repeat?

99

Estoy usando Primefaces en una aplicación JSF 2. Tengo un <p:dataTable>, y en lugar de seleccionar filas, quiero que el usuario pueda ejecutar directamente varias acciones en filas individuales. Para eso, tengo varios <p:commandLink>s en la última columna.

Mi problema: ¿cómo puedo pasar un ID de fila a la acción iniciada por el enlace de comando para saber en qué fila actuar? Intenté usar un <f:attribute>:

<p:dataTable value="#{bean.items}" var="item">
    ...
    <p:column>
        <p:commandLink actionListener="#{bean.insert}" value="insert">
            <f:attribute name="id" value="#{item.id}" />
        </p:commandLink>
    </p:column>
</p:dataTable>

Pero siempre da 0; aparentemente, la variable de fila fno está disponible cuando se representa el atributo (funciona cuando uso un valor fijo).

¿Alguien tiene una solución alternativa?

Michael Borgwardt
fuente

Respuestas:

215

En cuanto a la causa, <f:attribute>es específico del componente en sí (que se completa durante el tiempo de creación de la vista), no de la fila iterada (que se completa durante el tiempo de visualización).

Hay varias formas de lograr el requisito.

  1. Si su servletcontainer admite un mínimo de Servlet 3.0 / EL 2.2, simplemente páselo como argumento de acción / método de escucha del UICommand componente o AjaxBehavioretiqueta. P.ej

     <h:commandLink action="#{bean.insert(item.id)}" value="insert" />

    En combinación con:

     public void insert(Long id) {
         // ...
     }

    Esto solo requiere que el modelo de datos se conserve para la solicitud de envío del formulario. Lo mejor es poner el bean en el alcance de la vista mediante @ViewScoped.

    Incluso puede pasar todo el objeto del elemento:

     <h:commandLink action="#{bean.insert(item)}" value="insert" />

    con:

     public void insert(Item item) {
         // ...
     }

    En los contenedores Servlet 2.5, esto también es posible si proporciona una implementación EL que lo admita, como JBoss EL. Para obtener detalles sobre la configuración, consulte esta respuesta .


  2. Uso <f:param>en UICommandcomponente. Agrega un parámetro de solicitud.

     <h:commandLink action="#{bean.insert}" value="insert">
         <f:param name="id" value="#{item.id}" />
     </h:commandLink>

    Si su bean tiene un alcance de solicitud, deje que JSF lo configure por @ManagedProperty

     @ManagedProperty(value="#{param.id}")
     private Long id; // +setter

    O si su bean tiene un alcance más amplio o si desea una validación / conversión más detallada, úselo<f:viewParam> en la vista de destino, vea también f: viewParam vs @ManagedProperty :

     <f:viewParam name="id" value="#{bean.id}" required="true" />

    De cualquier manera, esto tiene la ventaja de que el modelo de datos no necesariamente debe conservarse para el envío del formulario (en el caso de que su bean tenga un alcance de solicitud).


  3. Uso <f:setPropertyActionListener>en UICommandcomponente. La ventaja es que esto elimina la necesidad de acceder al mapa de parámetros de solicitud cuando el bean tiene un alcance más amplio que el alcance de la solicitud.

     <h:commandLink action="#{bean.insert}" value="insert">
         <f:setPropertyActionListener target="#{bean.id}" value="#{item.id}" />
     </h:commandLink>

    En combinación con

     private Long id; // +setter

    Solo estará disponible por propiedad iden el método de acción. Esto solo requiere que el modelo de datos se conserve para la solicitud de envío del formulario. Lo mejor es poner el bean en el alcance de la vista mediante @ViewScoped.


  4. Enlaza el valor de la tabla de datos a lo DataModel<E>que a su vez envuelve los elementos.

     <h:dataTable value="#{bean.model}" var="item">

    con

     private transient DataModel<Item> model;
    
     public DataModel<Item> getModel() {
         if (model == null) {
             model = new ListDataModel<Item>(items);
         }
         return model;
     }

    ( transientHacerlo y crear una instancia perezosamente en el getter es obligatorio cuando está usando esto en un bean con alcance de vista o sesión, ya DataModelque no se implementa Serializable)

    Luego, podrá acceder a la fila actual DataModel#getRowData()sin pasar nada (JSF determina la fila según el nombre del parámetro de solicitud del enlace / botón de comando en el que se hizo clic).

     public void insert() {
         Item item = model.getRowData();
         Long id = item.getId();
         // ...
     }

    Esto también requiere que el modelo de datos se conserve para la solicitud de envío del formulario. Lo mejor es poner el bean en el alcance de la vista mediante @ViewScoped.


  5. Úselo Application#evaluateExpressionGet()para evaluar programáticamente el actual #{item}.

     public void insert() {
         FacesContext context = FacesContext.getCurrentInstance();
         Item item = context.getApplication().evaluateExpressionGet(context, "#{item}", Item.class);
         Long id = item.getId();
         // ...
     }

La forma de elegir depende de los requisitos funcionales y de si uno u otro ofrece más ventajas para otros fines. Personalmente, seguiría adelante con el n. ° 1 o, cuando también desee admitir contenedores de servlet 2.5, con el n. ° 2.

BalusC
fuente
1
+1, aunque mi preferencia va al n. ° 2 (si se debe admitir 2.5).
Bozho
Gracias por la exhaustiva respuesta. Desafortunadamente, tengo que informar que el n. ° 1 fue lo único que funcionó en una tabla de datos filtrada de primefaces (que es exactamente el escenario para el que lo necesito). Todos los demás solo trabajaron en una tabla sin filtrar. Sin embargo, veo esto más como un error en las caras principales que en su respuesta.
Michael Borgwardt
¿La solicitud de bean o la vista tienen un alcance?
BalusC
2
¿Con "filtrado" te refieres a como en este ejemplo de escaparate ? Los síntomas indican que la acción del filtro tiene lugar solo en el lado del cliente y que no se mantiene el modelo en el lado del servidor. No estoy seguro si esto es intencional. Siempre puedes dejar un informe de problemas.
BalusC
Tu publicación se encuentra entre las publicaciones más útiles que he leído. Usé el método 5 porque me veo obligado a usar el servlet 2.5. Mi pregunta ahora es si es posible enviar un parámetro con commandLink (como en su ejemplo) pero usando ajax.
Aditzu
11

En JSF 1.2 esto fue hecho por <f:setPropertyActionListener>(dentro del componente de comando). En JSF 2.0 (EL 2.2 para ser precisos, gracias a BalusC) es posible hacerlo así:action="${filterList.insert(f.id)}

Bozho
fuente
6
Esta característica no es específica de JSF 2.0 (que por sí sola puede ejecutarse en contenedores Servlet 2.5), sino de EL 2.2 (que es parte de Servlet 3.0).
BalusC
11

En mi página de vista:

<p:dataTable  ...>
<p:column>
<p:commandLink actionListener="#{inquirySOController.viewDetail}" 
               process="@this" update=":mainform:dialog_content"
           oncomplete="dlg2.show()">
    <h:graphicImage library="images" name="view.png"/>
    <f:param name="trxNo" value="#{item.map['trxNo']}"/>
</p:commandLink>
</p:column>
</p:dataTable>

frijol de respaldo

 public void viewDetail(ActionEvent e) {

    String trxNo = getFacesContext().getRequestParameterMap().get("trxNo");

    for (DTO item : list) {
        if (item.get("trxNo").toString().equals(trxNo)) {
            System.out.println(trxNo);
            setSelectedItem(item);
            break;
        }
    }
}
Arfan J
fuente
-1

Gracias a este sitio por Mkyong , la única solución que realmente trabajó para nosotros para pasar un parámetro estaba presente

<h:commandLink action="#{user.editAction}">
    <f:param name="myId" value="#{param.id}" />
</h:commandLink>

con

public String editAction() {

  Map<String,String> params = 
            FacesContext.getExternalContext().getRequestParameterMap();
  String idString = params.get("myId");
  long id = Long.parseLong(idString);
  ...
}

Técnicamente, eso no se puede pasar directamente al método en sí, sino al JSF request parameter map.

EpicPandaForce
fuente
1
Tiene un problema diferente al que se pregunta aquí. Desea conservar los parámetros de solicitud del #{param}mapa para solicitudes posteriores, no para pasar un parámetro arbitrario. Sus preguntas y respuestas están cubiertas por stackoverflow.com/questions/17734230
BalusC