novembre
2007
Bonjour.
Voici encore un petit exemple avec RichFaces.
Cette fois-ci, il s’agit de réaliser une petite application mettant en œuvre le drag and drop offert par RichFaces.
C’est une simple application composée d’une seule page et qui simule l’activité d’achat en ligne: quelques articles et un panneau. Pour acheter un article, il suffit de le dragger et le dropper dans le panneau.
Attaquons sans tarder la couche métier de notre application.
On commence par les entités (1 seul dans ce cas-ci):
- L’objet Item représente un article. Il contient juste un identifiant, un nom et un prix.
package model;
public class Item {
private String name;
private Long price;
private Long id;
public Item() {
}
public Item(String name, Long price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getPrice() {
return price;
}
public void setPrice(Long price) {
this.price = price;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
Passons maintenant au contrôleurs JSF. On en a deux:
- ItemCtrl: Un simple pojo qui expose une liste d’Items.
- CartCtrl: Contient lui une liste d’Items qui représente les articles achetés jusque là ainsi qu’un champ price qui contient le prix total des articles achetés.
De plus, c’est ce contrôleur qui gère l’évènement drop généré lorsque l’on dépose un article sur le panneau.
Voici le code d’ItemCtrl
package control;
import java.util.ArrayList;
import java.util.List;
import model.Item;
public class ItemsCtrl {
private List<item> items = new ArrayList<item>();
public ItemsCtrl() {
items.add(new Item("Montre", 50L));
items.add(new Item("Table", 450L));
items.add(new Item("Chaise", 150L));
items.add(new Item("Moquette", 250L));
items.add(new Item("Lit", 750L));
items.add(new Item("Armoire", 1050L));
items.add(new Item("Ordinateur", 750L));
items.add(new Item("Imprimante", 90L));
}
public List<item> getItems() {
return items;
}
public void setItems(List<item> items) {
this.items = items;
}
}
Rien de spécial à dire à propos de cette classe (juste un détail, dans le constructeur, j’ai ajouté quelques articles fictifs pour égayer un peu l’application !)
Maintenant, le code de CartCtrl:
package control;
import java.util.ArrayList;
import java.util.List;
import org.richfaces.event.DropEvent;
import model.Item;
public class CartCtrl {
private List<item> items = new ArrayList<item>();
private Long price = 0L;
public void onDropItem(DropEvent e) {
Item i = (Item) e.getDragValue();
items.add(i);
price += i.getPrice();
}
public List<item> getItems() {
return items;
}
public void setItems(List<item> items) {
this.items = items;
}
public Long getPrice() {
return price;
}
public void setPrice(Long price) {
this.price = price;
}
}
Le seul point à expliquer ici est la méthode onItemDrop.
Cette méthode ne retourne rien (void) et accepte un paramètre de type org.richfaces.event.DropEvent
qui contient toutes les informations relatives à un évènement Drag and Drop.
En particulier, on a le champ dragValue (Object) qui contient l’objet qu’on a déplacé et lâché au dessus du panneau. Ici, c’est un Item. Dès lors, on l’ajoute à la liste des articles achetés et on incrémente le prix total des achats.
Passons maintenant à la couche présentation.
Pour afficher la liste des articles, j’ai décidé d’utiliser le composant DataGrid de RichFaces qui ressemble au DataTable mais qui affiche les données exactement comme le composant PanelGrid de JSF:
<rich:dataGrid value="#{itemsCtrl.items}" var="item" columns="3">
<h:panelGrid border="0" columns="2">
<h:outputText value="Nom: " />
<h:outputText value="#{item.name}" />
<h:outputText value="Prix: " />
<h:outputText value="#{item.price}" />
</h:panelGrid>
</rich:dataGrid>
Voici ce que ça donne:
On va maintenant rendre les éléments draggables. Pour ce faire, il suffit d’ajouter le composant dragSupport de RichFaces dans chaque article. Comme le composant support d’ajax4Jsf (présenté dans les posts précédents), ce composant ajouté le comportement draggable au composant qui le contient.
Dans notre cas, on va placer le panelGrid sur lequel on itère dans le composant a4j:outputPanel (dragSupport impose quelques limitations sur le conteneur, et il se trouve que outputPanel les satisfait). On ajoutera ensuite dragSupport dans le outputPanel, ce qui donne:
<rich:dataGrid value="#{itemsCtrl.items}" var="item" columns="3">
<a4j:outputPanel layout="block" style="cursor: move">
<rich:dragSupport dragType="item" dragValue="#{item}" />
<h:panelGrid border="0" columns="2">
<h:outputText value="Nom: " />
<h:outputText value="#{item.name}" />
<h:outputText value="Prix: " />
<h:outputText value="#{item.price}" />
</h:panelGrid>
</a4j:outputPanel>
</rich:dataGrid>
Comme principaux paramètres, dragSupport prend:
- dragType: une chaine qui identifie le type de l’élément. C’est surtout utile pour pouvoir discerner entre plusieurs types d’éléments draggables. Ici, on en a qu’un seul, et on le nomm item.
- dragValue: la valeur de l’élément draggé. C’est cette valeur là qui sera récupéré dans DropEvent.getDragValue (cf plus haut). Ici, on met l’Item courant.
Reste maintenant à définir la zone de drop.
Comme pour le drag, on définit une telle zone en incluant le composant dropSupport:
<a4j:outputPanel layout="block">
<rich:dropSupport acceptedTypes="item" dropListener="#{cartCtrl.onDropItem}"
reRender="boughtItems, totalPrice" dropValue="x" />
<h:graphicImage url="/pics/shopping_cart.png" />
</a4j:outputPanel>
En gros, il s’agit d’une image à l’intérieur dans outputPanel pour les même raisons que pour le dragSupport).
Les attributs de dropSupport sont:
- acceptedType: un liste de types d’éléments acceptés par cette zone. Ici, on ne a qu’un seul qui est item (cf attribut dragType du composant dragSupport).
- dropListener: la méthode d’un managed bean qui sera appelée lorsque le drag and drop est terminé (cf CartCtrl.onDropItem).
- reRender: les identifiants des composants à re-dessiner à la réception de la réponse de la requête générée par le drop.
N.B: Il faut mettre les composants dragSupport (ainsi que ses parents) et dropSupport (ainsi que ses parents) dans un formulaire et dans le même formulaire !
Reste enfin à afficher les articles achetés ainsi que le prix total:
<rich:dataList id="boughtItems" value="#{cartCtrl.items}" var="item">
<h:outputText value="#{item.name}" />
</rich:dataList>
<h:outputText id="totalPrice" value="Prix total: #{cartCtrl.price}" />
Voilou ! C’est un peu long parce qu’on est passé par un exemple plus ou moins réaliste et pratique, mais c’est hyper-simple dans le fond !
Voici pour conclure un petit shot de l’application:
Ok merci!
Par contre je n’utilise pas IE6, je suis sous firefox 2.
Je viens de mettre un poitn d’arret dans la méthode onDropItem de la classe CartCtrl et je vois que je ne rentre jamais dans cette méthode. Ce qui est bizarre c’est que lorsque je fais le drag et que je drop sur le panier, le cadre change de couleur, donc apparemment il reconnait bien le drop, mais il ne se passe rien…
Bonjour loic et content que tu trouves ces exemples intéressants
Sinon, pour ton premier problème, je crois que ça vient d’IE 6, n’est ce pas ? Dans ce navigateur (le 7 aussi mlais je ne suis plus sur), le onchange n’est déclenché que si on appuie sur , alors qu’il suffit de taper dans Firefox.
Pour ton second problème, là, je trouve ça bizarre … j’ai testé ça sous IE6, et ça marchait …
Je peux te proposer de poster ce problème dans le forum JSF, et là, on va essayer de t’aider, ok ?
Merci beaucoup pour ces exemples très interessants.
J’ai cependant une question, j’ai suivi le premier exemple (le hello world en ajax) et celui ci, dans les 2 cas je n’ai aucune erreur dans les logs de tomcat, les pages s’affichent bien mais les fonctions ajax ne font rien. Je m’explique : pour le hello world, je suis obligé d’appuyer sur la touche entrée pour que mon nom s’affiche, et dans l’exemple du drag ‘n drop, la liste et le prix ne se mettent pas a jour lors que je fais glisser un produit sur le panier… Etrange, j’ai pourtant essayé avec différents navigateurs. Est ce que quelqu’un a rencontré le même problème?
Merci
Loic