Hands on Wicket – Partie 2

Wicket est un framework plutôt en rupture avec les frameworks web actuels. Ici pas de fichier XML qui définit la navigation, pas de librairies de tag spécifiques, juste de la simplicité et du pragmatisme … tout pour nous plaire ! Wicket a choisi de ne pas mélanger les genres : les pages se font en plain html et le développement, à proprement parler, se fait en pur Java.

Dans le précédent billet, nous nous étions arrêtés à la mise en place d’un projet Wicket. Aujourd’hui, regardons de plus près la construction des pages et la façon d’implémenter la logique de navigation avec les formulaires et les liens.

Construction des pages

La construction des pages se fait en deux étapes :

  • Création du fichier HTML
  • Création de la classe Java correspondante

Par défaut, Wicket suppose que le fichier HTML et la classe Java doivent être dans le même package et porter le même nom (sauf l’extension bien sûr) . Il est cependant possible de modifier ce comportement.

Le fichier HTML utilise exclusivement la syntaxe HTML sur laquelle on vient positionner des attributs "wicket:id" pour les éléments qui sont associés aux traitements métiers des pages. Les balises auxquelles on ajoute cet attribut représentent le contenu dynamique (que l’on veut traiter en Java). C’est par cet attribut que Wicket fera la correspondance entre les tags HTML et les composants créés en Java. Le code HTML ne contient rien d’autre que de la mise en page et mise en forme (classique HTML + CSS), la logique métier étant complètement implémentée en Java. Un bel exemple de "Separation of concerns".

Etudions un exemple pour bien comprendre ce mécanisme.

Imaginons le fichier HTML suivant avec un message que l’on veut dynamique, un champ texte et un bouton :


  
    

Dynamic Message goes here

Le passage à Wicket donnera :


  
    

Message goes here

On ajoute l’attribut wicket:id auquel on donne une valeur unique qui nous servira dans le code Java. Cet attribut "wicket:id" persiste par défaut dans le HTML même après traitement par Wicket. Il est cependant possible de demander à Wicket de supprimer ces attributs (voir ici) en production pour obtenir des pages HTML conformes au standard du W3C.

Il nous reste plus qu’à écrire la classe Java correspondante qui doit hériter de la classe WebPage :

public class HelloWorld extends WebPage
{
  public HelloWorld()
  {
    IModel messageModel = new Model("Hello World!");
    add(new Label("message", messageModel));
    add(new MessageForm("messageInputForm", messageModel));
  }

  private static final class MessageForm extends Form
  {
    public MessageForm(String id, IModel model)
    {
      super(id);
      add(new TextField("messageInput", model));
    }

    protected void onSubmit()
    {
      // Rien à faire ici, le modèle sera automatiquement mis à jour
    }
  }
}

On remarque que le code Java réutilise les ids qui apparaissent dans le code HTML. C’est via ces ids que le pont entre le Java et le HTML est fait.

Ensuite, la seconde chose que l’on note est l’imbrication des composants. Cette imbrication doit respecter celle qui existe dans le HTML. Ainsi puisque le formulaire contient le champ texte, dans le code Java, on doit retrouver cette contenance.

L’autre particularité à remarquer est l’utilisation de l’objet Model. Tous les composants Wicket ont un model qui permet de définir leur contenu : même un simple label utilise un Model pour afficher son body. Chacun des modèles doit implémenter l’interface IModel (voir paragraphe suivant).

Dans notre cas, le champ texte et le label partagent le même modèle. Ainsi, le contenu du label aura le même contenu que le champ texte. De plus, par défaut, la soumission d’un formulaire entraîne la mise à jour automatique du modèle avec les données du formulaire. La classe Form appelle simplement la méthode setObject(…) du modèle avec la donnée correspondante du formulaire. La mise à jour du champ causera donc la mise à jour du label après soumission du formulaire. Il est bien sûr possible d’ajouter des traitements dans la méthode onSubmit() du formulaire.

Je vous invite à faire un tour dans les exemples, la Javadoc et le wiki pour éplucher les différents composants et leur modèle respectif.

La notion de Model

Les composants Wicket reposent tous sur un modèle. Les composants accèdent aux données qu’ils doivent prendre en charge via le modèle qui leur est attaché. Ces modèles implémentent tous l’interface IModel. Ceci permet de garder le contrôle sur l’accès aux données par les composants. Il est à noter que les objets qui implémentent cette interface seront systématiquement stockés dans la session. Il faut donc faire attention à la façon dont nous implémentons les modèles pour éviter de surcharger la session.

Plusieurs modèles sont disponibles et fournis par Wicket :

  • Basic Models – Pour implémenter un modèle basique qui sera intégralement stocké dans la session. Pour implémenter ce modèle, vous pouvez utiliser simplement la classe Model.
  • Detachable Models – Ces modèles implémentent l’interface IDetachable et offrent la méthode detach(), qui permet d’être notifié quand Wicket n’a plus besoin du modèle, ainsi les données peuvent être libérées de la mémoire au plus tôt.
  • Nested Models – Les modèles peuvent aussi être imbriqués pour représenter une hiérarchie d’objets. Pour ce faire, il suffit d’implémenter la méthode getNestedModel() de l’interface IModel qui renvoie le modèle sous-jacent.
  • Property Models – La classe AbstractPropertyModel fournit un comportement par défaut pour les modèles basés sur des propriétés. La classe PropertyModel permet d’encapsuler un POJO et de donner accès aux propriétés de cet objet. C’est cette classe qu’on utilise pour associer l’attribut d’un objet à un champ de formulaire.
  • Compound Property Models – Ce type de modèle permet de changer le comportement du modèle en fonction du composant qui y accède. Les méthodes get et setObject prennent le composant en paramètre pour cela.

Le PropertyModel et le Model restent les deux modèles les plus courants.

Exemple : 

Label myLabel = new Label("myLabel", new Model("Hello world !"));
Person person = .... // Obtenir un objet Person
TextField myTextField = new TextField("myTextField",
	new PropertyModel(person, "name"));

La navigation dans Wicket

Avec Wicket, la navigation se code en Java. Ici, pas de fichier xml de configuration des mappings URL/Class. Une méthode principale est utilisée pour ceci, il s’agit de setResponsePage() disponible sur la classe Component. Cette méthode prend en paramètre soit une classe, soit une classe avec des paramètres, soit une instance de page (dans les deux cas, la classe passée en paramètre doit hériter de la classe Page).

  • setResponsePage(Class) permet de rediriger vers une page indépendante des données de la page courante. L’instanciation de la page et la gestion de son cycle de vie sont effectuées par Wicket.
  • setResponsePage(Class, PageParameters) redirige vers une page en lui passant des données. L’instanciation de la page et la gestion de son cycle de vie sont là aussi effectuées par Wicket.
  • setResponsePage(Page) La troisième (avec une instance de page passée en paramètre) permet de pointer vers une page que l’on aura instanciée nous-même et à laquelle on a passé des paramètres.
setResponsePage(MySecondPage.class);
setResponsePage(new MySecondPage(myTextField.getModel())); // On passe des données dynamiques à la page cible

Nous allons appeler cette méthode en répondant aux "événements" de l’application à la manière d’une application Swing (onClick, onSubmit, etc.) ou en spécifiant pour certains types de lien, la page à afficher en cas de click.

Il y a deux principaux moyens de naviguer dans Wicket :

  • les formulaires : Dans ce cas, on utilise (ou hérite de) la classe Form à laquelle on ajoute les composants du formulaire. Le traitement associé à la validation du formulaire est codé dans la méthode onSubmit(). C’est aussi dans cette méthode que l’on peut définir la page qui sera affichée après soumission du formulaire via la méthode setResponsePage().
/**
 * @see org.apache.wicket.markup.html.form.Form#onSubmit()
 */
public final void onSubmit() {
	// Do the submit stuff (update the database, etc.)
	setResponsePage(MySecondPage.class);
}
  • les liens : Les liens héritent en général de la classe Link qui fournit les mécanismes de base pour créer un lien. Pour définir l’action à effectuer lors d’un click, on redéfinit simplement le méthode onClick(). On peut effectuer dans cette méthode un traitement pour finir par donner la page à afficher en résultat via la méthode setResponsePage(). Il est aussi possible d’utiliser la classe PageLink qui redirige simplement vers une page donnée en paramètres.
Link addToCartLink = new Link("addToCartLink") {
	private static final long serialVersionUID = 1L;
	public void onClick() {
		AddToCartPage page = new AddToCartPage(book.getId());
		setResponsePage(page);
	}
};
 // ou
new PageLink("viewLink", ViewPage.class);

Par défaut, les liens ne peuvent être ajoutés aux favoris (si on retourne sur une page "bookmarkée", on obtient un message "Page expired"). Les liens sont en fait maintenus en session, ce qui explique la possibilité de créer ses liens en Java de cette façon. Pour créer un lien "bookmarkable", il faut utiliser la classe du même nom : BookmarkablePageLink. Ce type de lien peut être ajouté aux favoris.

C’est tout pour cette fois, il vous faudra un peu de patience avant d’en savoir plus sur les fonctionnalités de Wicket.

3 commentaires

  • Merci pour ces billets sur ce fwk très intéressant.
    Personnellement je le rapproche de gwt, plus encore que de tapestry : manipulation depuis java, décoration … Le résultat est très différent, mais la démarche est proche, amha.
    En tout cas il ravit les swingologues

  • très cool billets , je vous remercie infiniment !

Laisser un commentaire