J'utilise Primefaces dans une application JSF 2. J'ai un <p:dataTable>
, et au lieu de sélectionner des lignes, je souhaite 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 transmettre un ID 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 il produit toujours 0 - apparemment, la variable de ligne f
n'est pas disponible lorsque l'attribut est rendu (cela fonctionne lorsque j'utilise une valeur fixe).
Quelqu'un a une solution alternative?
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 y a plusieurs façons de satisfaire à l'exigence.
Utilisez <f:param>
à la place. 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 est limité à la demande, laissez JSF le régler par @ManagedProperty
@ManagedProperty(value="#{param.id}")
private Long id; // +setter
Ou si votre bean a une portée plus large ou si vous souhaitez une validation/conversion plus fine, utilisez <f:viewParam>
sur la vue cible, voir aussi f: viewParam vs @ManagedProperty :
<f:viewParam name="id" value="#{bean.id}" required="true" />
Quoi qu'il en soit, cela présente l'avantage que le modèle de données n'a pas nécessairement besoin d'être préservé pour l'envoi du formulaire (dans le cas où votre bean est soumis à une requête).
Utilisez <f:setPropertyActionListener>
à la place. L'avantage est que cela élimine la nécessité d'accéder à la mappe de paramètres de demande lorsque le bean a une portée plus large que celle de la demande.
<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 ne sera disponible que par la propriété id
dans la méthode action. Cela nécessite seulement que le modèle de données soit préservé pour la demande de soumission de formulaire. Le mieux est de mettre le bean dans la portée de la vue par @ViewScoped
.
Si votre servletcontainer prend en charge Servlet 3.0/EL 2.2, transmettez-le simplement en tant qu'argument de méthode. Cela nécessite également que le modèle de données soit préservé pour la demande de soumission de formulaire. Le mieux est de mettre le bean dans la portée de la vue par @ViewScoped
.
<h:commandLink action="#{bean.insert(item.id)}" value="insert" />
En combinaison avec:
public void insert(Long id) {
// ...
}
Vous pouvez même transmettre l'objet item entier:
<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 le supporte, comme en tant que JBoss EL. Pour plus de détails sur la configuration, voir cette réponse .
Liez la valeur datatable à DataModel<E>
à la place, ce qui à son tour enveloppe 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 rendant transient
et son instancier paresseux dans le getter est obligatoire lorsque vous utilisez cette option sur une vue ou une session, puisque DataModel
ne le permet pas. implémenter Serializable
)
Ensuite, vous pourrez accéder à la ligne en cours par DataModel#getRowData()
sans rien transmettre (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 préservé pour la demande de soumission de formulaire. Le mieux est de mettre le bean dans la portée de la vue par @ViewScoped
.
Vous pouvez utiliser Application#evaluateExpressionGet()
pour évaluer par programme le #{item}
Actuel.
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, je choisirais le numéro 3 ou, lorsque vous souhaitez également prendre en charge les conteneurs Servlet 2.5, le numéro 2.
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)}
Dans ma page de vue:
<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 sec
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;
}
}
}