Comment puis-je passer la ligne sélectionnée à commandLink dans dataTable ou ui: repeat?

99

J'utilise Primefaces dans une application JSF 2. J'ai un <p:dataTable>, et au lieu de sélectionner des lignes, je veux que l'utilisateur puisse exécuter directement diverses actions sur des lignes individuelles. Pour cela, j'ai plusieurs <p:commandLink>s dans la dernière colonne.

Mon problème: comment puis-je passer un identifiant de ligne à l'action lancée par le lien de commande afin de savoir sur quelle ligne agir? J'ai essayé d'utiliser 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>

Mais cela donne toujours 0 - apparemment, la variable de ligne fn'est pas disponible lorsque l'attribut est rendu (cela fonctionne lorsque j'utilise une valeur fixe).

Quelqu'un a-t-il une solution alternative?

Michael Borgwardt
la source

Réponses:

215

En ce qui concerne la cause, le <f:attribute>est spécifique au composant lui-même (renseigné lors de la création de la vue), et non à la ligne itérée (renseigné lors du rendu de la vue).

Il existe plusieurs façons de répondre à l'exigence.

  1. Si votre servletcontainer prend en charge un minimum de Servlet 3.0 / EL 2.2, passez-le simplement comme argument de la méthode action / listener du UICommand composant ou de la AjaxBehaviorbalise. Par exemple

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

    En combinaison avec:

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

    Cela nécessite uniquement que le modèle de données soit conservé pour la demande d'envoi de formulaire. Le mieux est de placer le bean dans la portée de la vue par @ViewScoped.

    Vous pouvez même transmettre tout l'objet d'élément:

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

    avec:

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

    Sur les conteneurs Servlet 2.5, cela est également possible si vous fournissez une implémentation EL qui prend en charge cela, comme JBoss EL. Pour plus de détails sur la configuration, consultez cette réponse .


  2. Utilisation <f:param>en UICommandcomposant. Il ajoute un paramètre de requête.

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

    Si votre bean a une portée de requête, laissez JSF le définir par @ManagedProperty

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

    Ou si votre bean a une portée plus large ou si vous voulez une validation / conversion plus fine, utilisez-la <f:viewParam>sur la vue cible, voir aussi f: viewParam vs @ManagedProperty :

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

    Dans tous les cas, cela présente l'avantage que le modèle de données n'a pas nécessairement besoin d'être conservé pour l'envoi du formulaire (dans le cas où votre bean est limité à la requête).


  3. Utilisation <f:setPropertyActionListener>en UICommandcomposant. L'avantage est que cela supprime le besoin d'accéder à la mappe de paramètres de requête lorsque le bean a une portée plus large que la portée de la requête.

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

    En combinaison avec

     private Long id; // +setter

    Il sera uniquement disponible par propriété iddans la méthode d'action. Cela nécessite uniquement que le modèle de données soit conservé pour la demande d'envoi de formulaire. Le mieux est de placer le bean dans la portée de la vue par @ViewScoped.


  4. Liez la valeur datatable à la DataModel<E>place qui à son tour encapsule les éléments.

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

    avec

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

    (le créer transientet l'instancier paresseusement dans le getter est obligatoire lorsque vous l'utilisez sur un bean à portée de vue ou de session car il DataModeln'est pas implémenté Serializable)

    Ensuite, vous pourrez accéder à la ligne actuelle DataModel#getRowData()sans rien passer (JSF détermine la ligne en fonction du nom du paramètre de demande du lien / bouton de commande cliqué).

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

    Cela nécessite également que le modèle de données soit conservé pour la demande d'envoi de formulaire. Le mieux est de placer le bean dans la portée de la vue par @ViewScoped.


  5. Utilisez Application#evaluateExpressionGet()pour évaluer le courant par programme #{item}.

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

La manière de choisir dépend des exigences fonctionnelles et du fait que l'une ou l'autre offre plus d'avantages à d'autres fins. Personnellement, j'irais de l'avant avec le n ° 1 ou, si vous souhaitez également prendre en charge les conteneurs de servlet 2.5, avec le n ° 2.

BalusC
la source
1
+1, même si ma préférence va au n ° 2 (si 2.5 doit être pris en charge).
Bozho le
Merci pour la réponse exhaustive. Malheureusement, je dois signaler que le n ° 1 était la seule chose qui fonctionnait dans un datatable primefaces filtré (ce qui est exactement le scénario pour lequel j'en ai besoin). Tous les autres ne travaillaient que sur une table non filtrée. Je vois cela plus comme un bug dans les primefaces que dans votre réponse, cependant.
Michael Borgwardt
La demande ou la vue de bean est-elle limitée?
BalusC
2
Avec "filtré", vous voulez dire comme dans cet exemple de vitrine ? Les symptômes indiquent que l'action de filtrage a lieu uniquement côté client et que le modèle côté serveur n'est pas conservé. Je ne sais pas si c'est intentionnel. Vous pouvez toujours laisser un rapport de problème.
BalusC
Votre message fait partie des articles les plus utiles que j'ai jamais lus J'ai utilisé la méthode 5 car je suis obligé d'utiliser le servlet 2.5. Ma question est maintenant de savoir s'il est possible d'envoyer un paramètre avec commandLink (comme dans votre exemple) mais en utilisant ajax?
Aditzu
11

Dans JSF 1.2, cela a été fait par <f:setPropertyActionListener>(dans le composant de commande). Dans JSF 2.0 (EL 2.2 pour être précis, grâce à BalusC), il est possible de le faire comme ceci:action="${filterList.insert(f.id)}

Bozho
la source
6
Cette fonctionnalité n'est pas spécifique à JSF 2.0 (qui peut s'exécuter par lui-même dans des conteneurs Servlet 2.5), mais à EL 2.2 (qui fait partie de Servlet 3.0).
BalusC
11

Dans ma page d'affichage:

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

haricot de soutien

 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
la source
-1

Merci à ce site par Mkyong , la seule solution qui fait passer un paramètre a travaillé pour nous cette

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

avec

public String editAction() {

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

Techniquement, vous ne pouvez pas passer directement à la méthode elle-même, mais au JSF request parameter map.

EpicPandaForce
la source
1
Vous avez un problème différent de celui demandé ici. Vous souhaitez conserver les paramètres de requête de la #{param}carte pour les requêtes suivantes, et non pour transmettre un paramètre arbitraire. Vos questions et réponses sont couvertes par stackoverflow.com/questions/17734230
BalusC