Les tableaux HTML sont plus lisibles lorsque les lignes paires sont facilement différenciables des lignes impaires, par exemple grâce à un fond de couleur différente.
Voyons comment Wicket permet d'industrialiser ce traitement, afin qu'il soit facilement applicable à n'importe quel tableau.
Pour l'exemple, nous afficherons la liste des Locales suportées par la JVM (langage et pays).
Afficher un tableau avec Wicket
Tout d'abord, rappelons comment on affiche un tableau en Wicket.
Du point de vue HTML, il suffit d'utiliser les balises standard : <table>, <tr>, <td>.
Comme c'est l'élément <tr> (et tout son contenu) qui sera répété pour chaque Locale à afficher, c'est lui qui porte l'attribut "wicket:id".
<table> <tr> <th>Country</th> <th>Language</th> </tr> <tr wicket:id="localesList"> <td wicket:id="localeCountry">Kronos</td> <td wicket:id="localeLanguage">Klingon</td> </tr> </table>
Du point de vue Java, ce n'est guère plus compliqué grâce au composant ListView.
Comme tout composant Wicket, il respecte l'architecture MVC :
- Un Modèle (
IModel) doit lui être fourni - ici, la liste des Locales disponibles ; - Le
ListViewlui-même encapsule la logique du Contrôleur, en bouclant sur les éléments du Modèle ; - La Vue est construite dynamiquement par la méthode
populateItem(), sur laquelle nous nous concentrerons plus loin.
Voici donc le squelette de notre Page :
public class StripedTablePage extends WebPage { /** A list of Locales */ List<Locale> locales = Arrays.asList(Locale.getAvailableLocales()); /** Constructor */ public StripedTablePage() { ListView<Locale> localesList = new ListView<Locale>("localesList", locales) { @Override protected void populateItem(final ListItem<Locale> item) { // TODO : build the View } }; add(localesList); } }
Le paramètre "item" de la méthode "populateItem()" a deux utilités :
- Il donne accès à l'élément courant :
Object element = item.getModelObject()) ; - Il représente la racine des composants de la Vue, qui doivent donc y être attachés :
item.add(new Label(...)).
Dans notre cas, deux Labels servent à afficher la langue et le pays de chaque Locale. Voici donc la méthode complétée :
@Override protected void populateItem(final ListItem<Locale> item) { // Retrieve the current Locale final Locale loc = item.getModelObject(); // Add a Label for the Country item.add(new Label("localeCountry", new AbstractReadOnlyModel<String>() { @Override public String getObject() { return loc.getDisplayCountry(loc); } })); // Add a Label for the Language item.add(new Label("localeLanguage", new AbstractReadOnlyModel<String>() { @Override public String getObject() { return loc.getDisplayLanguage(loc); } })); }
Amélioration du rendu avec les styles CSS
Afin de différencier les lignes paires et impaires, nous allons leur associer des classes CSS différentes :
.evenRow {background-color: #FFF;} .oddRow {background-color: #DDF;}
Mais comment les appliquer aux balises <tr> ?
On pourrait éventuellement sous-classer le composant ListView pour contrôler le code HTML généré, mais cette solution est techniquement complexe et peu souple.
Nous utiliserons plutôt le système de "Behaviors" de Wicket : implémentant le design pattern Decorator, un Behavior encapsule un traitement particulier et peut être facilement appliqué à tout composant compatible.
Wicket en fournit justement deux, spécialisés dans la manipulation des attributs des balises HTML : AttributeModifier et AttributeAppender. Nous nous appuierons sur ce dernier pour ajouter la bonne classe CSS nos balises <tr>.
Pour fonctionner, notre Behavior a besoin du numéro de la ligne courante et des noms des classes CSS à appliquer. Tout le traitement proprement dit est réalisé par l'AttributeAppender dont on hérite :
public class AlternateRowCssClassAttributeAppender extends AttributeAppender { public static final String ATTRIBUTE_NAME = "class"; public static final String VALUE_SEPARATOR = " "; public AlternateRowCssClassAttributeAppender(final int index, final String evenRowCssClass, final String oddRowCssClass) { super( ATTRIBUTE_NAME, true, new AbstractReadOnlyModel<String>() { @Override public String getObject() { // Our algorithm return (index % 2 == 0) ? evenRowCssClass : oddRowCssClass; } }, VALUE_SEPARATOR ); } }
Pour finir, il reste à l'appliquer à chaque ligne du tableau, lors de la construction de la Vue :
@Override protected void populateItem(final ListItem<Locale> item) { // Retrieve the current Locale and add the Labels (...) // Apply the Behavior to colorize each other line item.add(new AlternateRowCssClassAttributeAppender(item.getIndex(), "evenRow", "oddRow")); }
Le code complet de notre page d'exemple devient donc :
public class StripedTablePage extends WebPage { /** A list of Locales */ List<Locale> locales = Arrays.asList(Locale.getAvailableLocales()); /** Constructor */ public StripedTablePage() { ListView<Locale> localesList = new ListView<Locale>("localesList", locales) { @Override protected void populateItem(final ListItem<Locale> item) { // Apply the Behavior to colorize each other line item.add(new AlternateRowCssClassAttributeAppender(item.getIndex(), "evenRow", "oddRow")); // Retrieve the current Locale final Locale loc = item.getModelObject(); // Add a Label for the Country item.add(new Label("localeCountry", new AbstractReadOnlyModel<String>() { @Override public String getObject() { return loc.getDisplayCountry(loc); } })); // Add a Label for the Language item.add(new Label("localeLanguage", new AbstractReadOnlyModel<String>() { @Override public String getObject() { return loc.getDisplayLanguage(loc); } })); } }; add(localesList); } }
Conclusion
Après avoir revu la façon dont Wicket gérait les tableaux, nous avons développé un Behavior personnalisé permettant d'appliquer des styles CSS différents aux lignes paires et impaires.
Vous pouvez désormais l'ajouter à votre librairie de composants réutilisables !
Le code source de l'article est disponible en pièce jointe de ce billet.
Le script Gradle fourni permet de le compiler et de lancer un serveur Jetty intégré :
gradle jettyRun
L'application est alors accessible à l'adresse suivante : http://localhost:8080/Wicket-tips/


Commentaires
Bonjour,
Merci pour l'article mais une petite capture du résultat serait pratique.
Voir des tableaux sexy ;-)
Hello, thanks for the article, reads very interesting. Please, could you attach a screenshot of the resulting table?
The URL you wrote at article end is on "localhost" so it's reachable only for you...
Hello,
I'll attach a screenshot soon, but don't expect to be amazed : it's only a striped table (odd and even lines with different backgrounds).
As for the localhost URL, it's not an error : it's where you must point your browser to after you've followed the instructions to install and run the attached code sample.
J'ai une petite question à des <th> du tableau qui est généré. Si ta liste est vide tu va constuire un tableau avec seulement des <th>. Admettons que je souhaite rendre le tableau invisible si la liste est vide (setVisible(false)), j'aurai toujours ses <th> qui vont trainer. Ne serait-ce pas possible de les définir dans le code Java de la ListView ?