Come posso passare la riga selezionata a commandLink all'interno di dataTable o ui: repeat?


99

Sto usando Primefaces in un'applicazione JSF 2. Ho un <p:dataTable>e invece di selezionare le righe, voglio che l'utente sia in grado di eseguire direttamente varie azioni sulle singole righe. Per questo, ho diversi messaggi <p:commandLink>nell'ultima colonna.

Il mio problema: come posso passare un ID riga all'azione avviata dal collegamento di comando in modo da sapere su quale riga agire? Ho provato a utilizzare 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>

Ma restituisce sempre 0 - apparentemente la variabile di riga fnon è disponibile quando l'attributo viene visualizzato (funziona quando uso un valore fisso).

Qualcuno ha una soluzione alternativa?

Risposte:


215

Per quanto riguarda la causa, <f:attribute>è specifico del componente stesso (popolato durante il tempo di creazione della vista), non della riga iterata (popolata durante il tempo di rendering della vista).

Esistono diversi modi per soddisfare il requisito.

  1. Se il tuo servletcontainer supporta un minimo di Servlet 3.0 / EL 2.2, passalo come argomento del metodo action / listener del UICommand componente o del AjaxBehaviortag. Per esempio

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

    In combinazione con:

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

    Ciò richiede solo che il modello di dati venga conservato per la richiesta di invio del modulo. La cosa migliore è mettere il bean nell'area di visualizzazione di @ViewScoped.

    Puoi anche passare l'intero oggetto oggetto:

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

    con:

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

    Sui contenitori Servlet 2.5, questo è possibile anche se fornisci un'implementazione EL che lo supporta, come JBoss EL. Per i dettagli sulla configurazione, vedere questa risposta .


  2. Utilizzare <f:param>nel UICommandcomponente. Aggiunge un parametro di richiesta.

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

    Se il tuo bean ha l'ambito della richiesta, lascia che JSF lo imposti da @ManagedProperty

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

    Oppure, se il tuo bean ha un ambito più ampio o se desideri una convalida / conversione più dettagliata, usalo<f:viewParam> nella vista di destinazione, vedi anche f: viewParam vs @ManagedProperty :

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

    In ogni caso, questo ha il vantaggio che il datamodel non deve necessariamente essere conservato per l'invio del modulo (nel caso in cui il tuo bean abbia lo scope della richiesta).


  3. Utilizzare <f:setPropertyActionListener>nel UICommandcomponente. Il vantaggio è che ciò elimina la necessità di accedere alla mappa dei parametri della richiesta quando il bean ha un ambito più ampio rispetto all'ambito della richiesta.

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

    In combinazione con

     private Long id; // +setter

    Sarà disponibile solo per proprietà idnel metodo di azione. Ciò richiede solo che il modello di dati venga conservato per la richiesta di invio del modulo. La cosa migliore è mettere il bean nell'area di visualizzazione di @ViewScoped.


  4. Associa invece il valore datatable DataModel<E>che a sua volta avvolge gli elementi.

     <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;
     }

    ( transientcrearlo e istanziarlo pigramente nel getter è obbligatorio quando lo si utilizza su una vista o un bean con ambito di sessione poiché DataModelnon implementa Serializable)

    Quindi sarai in grado di accedere alla riga corrente DataModel#getRowData()senza passare nulla (JSF determina la riga in base al nome del parametro di richiesta del link / pulsante di comando cliccato).

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

    Ciò richiede anche che il modello di dati venga conservato per la richiesta di invio del modulo. La cosa migliore è mettere il bean nell'area di visualizzazione di @ViewScoped.


  5. Utilizzare Application#evaluateExpressionGet()per valutare a livello di codice la corrente #{item}.

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

Quale modo scegliere dipende dai requisiti funzionali e se l'uno o l'altro offre più vantaggi per altri scopi. Personalmente andrei avanti con il numero 1 o, quando desideri supportare anche i contenitori servlet 2.5, con il numero 2.


1
+1, anche se la mia preferenza va al n. 2 (se 2.5 deve essere supportato).
Bozho

Grazie per la risposta esauriente. Sfortunatamente, devo segnalare che # 1 era l'unica cosa che ha funzionato in un primefaces filtrato databile (che è esattamente lo scenario per cui ho bisogno). Tutti gli altri hanno lavorato solo su un tavolo non filtrato. Lo vedo più come un bug nelle prime facce che nella tua risposta, però.
Michael Borgwardt

La richiesta o la vista del bean ha un ambito?
BalusC

2
Con "filtrato" intendi come in questo esempio di vetrina ? I sintomi indicano che l'azione del filtro viene eseguita solo sul lato client e che il modello sul lato server non viene mantenuto. Non sono sicuro che sia intenzionale. Puoi sempre lasciare un rapporto sul problema.
BalusC

Il tuo post è tra i post più utili che abbia mai letto. Ho usato il metodo 5 perché sono costretto a usare servlet 2.5. La mia domanda ora è se è possibile inviare un parametro con commandLink (come nel tuo esempio) ma usando ajax?
Aditzu

11

In JSF 1.2 questo veniva fatto da <f:setPropertyActionListener>(all'interno del componente di comando). In JSF 2.0 (EL 2.2 per la precisione, grazie a BalusC) è possibile farlo in questo modo:action="${filterList.insert(f.id)}


6
Questa caratteristica non è specifica per JSF 2.0 (che può essere eseguito da solo in contenitori Servlet 2.5), ma per EL 2.2 (che fa parte di Servlet 3.0).
BalusC

11

Nella mia pagina di visualizzazione:

<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>

fagiolo di sostegno

 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;
        }
    }
}

-1

Grazie a questo sito di Mkyong , l'unica soluzione che effettivamente ha funzionato per noi per passare un parametro è stata questa

<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);
  ...
}

Tecnicamente, che non puoi passare direttamente al metodo stesso, ma al JSF request parameter map.


1
Hai un problema diverso da quello richiesto qui. Si desidera conservare i parametri della richiesta dalla #{param}mappa per le richieste successive, non per passare un parametro arbitrario. Il tuo Q & A è coperto da stackoverflow.com/questions/17734230
BalusC
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.