Wie kann ich die ausgewählte Zeile an commandLink in dataTable oder ui: repeat übergeben?

99

Ich verwende Primefaces in einer JSF 2-Anwendung. Ich habe eine <p:dataTable>und anstatt Zeilen auszuwählen, möchte ich, dass der Benutzer verschiedene Aktionen für einzelne Zeilen direkt ausführen kann. Dafür habe ich <p:commandLink>in der letzten Spalte mehrere s.

Mein Problem: Wie kann ich eine Zeilen-ID an die über den Befehlslink gestartete Aktion übergeben, damit ich weiß, auf welche Zeile ich reagieren soll? Ich habe versucht, ein <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>

Aber es gibt immer 0 - anscheinend ist die Zeilenvariable fnicht verfügbar, wenn das Attribut gerendert wird (es funktioniert, wenn ich einen festen Wert verwende).

Hat jemand eine alternative Lösung?

Michael Borgwardt
quelle

Antworten:

215

Die Ursache <f:attribute>ist spezifisch für die Komponente selbst (während der Erstellungszeit der Ansicht ausgefüllt) und nicht für die iterierte Zeile (während der Renderzeit der Ansicht ausgefüllt).

Es gibt verschiedene Möglichkeiten, um die Anforderung zu erfüllen.

  1. Wenn Ihr Servletcontainer mindestens Servlet 3.0 / EL 2.2 unterstützt, übergeben Sie ihn einfach als Argument für die Aktion / Listener-Methode der UICommand Komponente oder des AjaxBehaviorTags. Z.B

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

    In Kombination mit:

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

    Dies erfordert nur, dass das Datenmodell für die Formularübermittlungsanforderung erhalten bleibt. Am besten setzen Sie die Bean in den Ansichtsbereich von @ViewScoped.

    Sie können sogar das gesamte Objekt übergeben:

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

    mit:

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

    Auf Servlet 2.5-Containern ist dies auch möglich, wenn Sie eine EL-Implementierung bereitstellen, die dies unterstützt, z. B. JBoss EL. Einzelheiten zur Konfiguration finden Sie in dieser Antwort .


  2. Verwendung <f:param>in UICommandKomponenten. Es wird ein Anforderungsparameter hinzugefügt.

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

    Wenn Ihre Bean einen Anforderungsbereich hat, lassen Sie sie von JSF festlegen @ManagedProperty

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

    Oder wenn Ihre Bean einen breiteren Bereich hat oder wenn Sie eine feinkörnigere Validierung / Konvertierung wünschen, verwenden Sie sie <f:viewParam>in der Zielansicht , siehe auch f: viewParam vs @ManagedProperty :

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

    In beiden Fällen hat dies den Vorteil, dass das Datenmodell nicht unbedingt für das Senden des Formulars beibehalten werden muss (für den Fall, dass Ihre Bean einen Anforderungsbereich hat).


  3. Verwendung <f:setPropertyActionListener>in UICommandKomponenten. Der Vorteil besteht darin, dass der Zugriff auf die Anforderungsparameterzuordnung nicht mehr erforderlich ist, wenn die Bean einen breiteren Bereich als den Anforderungsbereich hat.

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

    In Kombination mit

     private Long id; // +setter

    Es ist nur nach Eigenschaft idin Aktionsmethode verfügbar . Dies erfordert nur, dass das Datenmodell für die Formularübermittlungsanforderung erhalten bleibt. Am besten setzen Sie die Bean in den Ansichtsbereich von @ViewScoped.


  4. Binden Sie DataModel<E>stattdessen den datierbaren Wert an, der wiederum die Elemente umschließt.

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

    mit

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

    ( transientWenn Sie dies in einer Bean mit Ansicht oder Sitzungsbereich verwenden, ist es obligatorisch, es im Getter zu erstellen und träge zu instanziieren, da DataModeles nicht implementiert wird. Serializable)

    Anschließend können Sie auf die aktuelle Zeile zugreifen, DataModel#getRowData()indem Sie nichts weitergeben (JSF ermittelt die Zeile anhand des Anforderungsparameternamens des angeklickten Befehlslinks / der angeklickten Schaltfläche).

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

    Dies erfordert auch, dass das Datenmodell für die Formularübermittlungsanforderung beibehalten wird. Am besten setzen Sie die Bean in den Ansichtsbereich von @ViewScoped.


  5. Verwenden Sie Application#evaluateExpressionGet()diese Option, um den Strom programmgesteuert auszuwerten #{item}.

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

Welche Art zu wählen ist, hängt von den funktionalen Anforderungen ab und davon, ob die eine oder andere mehr Vorteile für andere Zwecke bietet. Ich persönlich würde mit # 1 fortfahren, oder, wenn Sie auch Servlet 2.5-Container unterstützen möchten, mit # 2.

BalusC
quelle
1
+1, obwohl meine Präferenz auf # 2 geht (wenn 2.5 unterstützt werden muss).
Bozho
Vielen Dank für die ausführliche Antwort. Leider muss ich berichten, dass # 1 das einzige war, was in einer gefilterten Primefaces-Datentabelle funktioniert hat (was genau das Szenario ist, für das ich es brauche). Alle anderen arbeiteten nur an einem ungefilterten Tisch. Ich sehe dies jedoch eher als Fehler in Primefaces als in Ihrer Antwort.
Michael Borgwardt
Ist die Bean-Anforderung oder -Ansicht gültig?
BalusC
2
Mit "gefiltert" meinen Sie wie in diesem Schaufensterbeispiel ? Die Symptome weisen darauf hin, dass die Filteraktion nur auf der Clientseite stattfindet und das Modell auf der Serverseite nicht verwaltet wird. Ich bin mir nicht sicher, ob dies beabsichtigt ist. Sie können jederzeit einen Problembericht hinterlassen.
BalusC
Ihr Beitrag gehört zu den nützlichsten Beiträgen, die ich je gelesen habe. Ich habe Methode 5 verwendet, weil ich gezwungen bin, Servlet 2.5 zu verwenden. Meine Frage ist nun, ob es möglich ist, einen Parameter mit commandLink (wie in Ihrem Beispiel) zu senden, aber mit Ajax?
Aditzu
11

In JSF 1.2 wurde dies von <f:setPropertyActionListener>(innerhalb der Befehlskomponente) durchgeführt. In JSF 2.0 (genauer gesagt EL 2.2 dank BalusC) ist dies folgendermaßen möglich:action="${filterList.insert(f.id)}

Bozho
quelle
6
Diese Funktion ist nicht spezifisch für JSF 2.0 (das selbst in Servlet 2.5-Containern ausgeführt werden kann), sondern für EL 2.2 (das Teil von Servlet 3.0 ist).
BalusC
11

In meiner Ansichtsseite:

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

Backing Bean

 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.
quelle
-1

Dank dieser Seite von Mkyong war die einzige Lösung, die tatsächlich für uns funktioniert hat, um einen Parameter zu übergeben, diese

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

mit

public String editAction() {

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

Technisch gesehen können Sie nicht direkt an die Methode selbst übergeben, sondern an die JSF request parameter map.

EpicPandaForce
quelle
1
Sie haben ein anderes Problem als hier gefragt. Sie möchten Anforderungsparameter aus der #{param}Zuordnung für nachfolgende Anforderungen beibehalten und keinen beliebigen Parameter übergeben. Ihre Fragen und Antworten werden
BalusC