79. AJAX Partie 12 : Le développement d'applications avec Spring Imprimer Sommaire Consulter avec table des matières
Développons en Java   v 2.10  
Copyright (C) 1999-2016 .  

 

80. GWT (Google Web Toolkit)

 

chapitre 8 0

 

Niveau : niveau 4 Supérieur 

 

GWT (Google Web Toolkit) est un framework open source de développement d'applications web mettant en oeuvre AJAX et développé par Bruce Johnson et Google.

Mi-2006, Google a diffusé GWT qui est un outil de développement d'applications de type RIA offrant une mise en oeuvre novatrice : le but est de faciliter le développement d'applications web mettant en oeuvre Ajax en faisant abstraction des incompatibilités des principaux navigateurs.

GWT propose de nombreuses fonctionnalités pour développer une application exécutable dans un navigateur et présetnant des comportements similaires à ceux d'une application desktop :

L'utilisation de GWT présente plusieurs avantages :

Le site officiel de GWT est à l'url http://code.google.com/webtoolkit/

Ce chapitre contient plusieurs sections :

 

80.1. La présentation de GWT

Le code de l'application est entièrement écrit en Java notamment la partie cliente qui devra s'exécuter dans un navigateur. Ce code Java n'est pas compilé en bytecode mais en JavaScript ce qui permet son exécution dans un navigateur.

Le coeur de GWT est donc composé du compilateur de code Java en JavaScript. L'avantage du code JavaScript produit est qu'il est capable de s'exécuter sur les principaux navigateurs sans adaptation particulière du source Java puisque le compilateur crée un fichier JavaScript optimisé pour chacun de ces navigateurs.

Le code à écrire pour la partie cliente est composé de plusieurs éléments :

La partie graphique d'une application GWT est composée d'une petite partie en HTML, de CSS et surtout de classes Java dans lesquelles des composants sont utilisés avec des gestionnaires d'événements pour définir l'interface de l'application et les réponses aux actions des utilisateurs.

GWT propose un ensemble assez complet de composants graphiques nommés widgets fournis en standard : l'ensemble des widgets inclut des composants graphiques standards (boutons, zones de saisie de texte, listes déroulantes, ...) mais contient aussi des composants plus riches tels que des panneaux déroulants, des onglets, des arbres, des boîtes de dialogues, .... Il est aussi possible de créer ses propres composants ou d'intégrer des frameworks JavaScript (ext, Dojo, Rialto, Yahoo UIL, ...)

Le code Java pour développer en GWT est très ressemblant à celui à produire pour développer une application graphique utilisant AWT :

Exemple :
public class TestBonjour implements EntryPoint {
    public void onModuleLoad() {
        Button bouton = new Button("Saluer", new ClickListener() {
            public void onClick(Widget sender) {
                Window.alert("Bonjour");
            }
        });
        RootPanel.get().add(bouton);
    }
}

GWT propose aussi des outils pour assurer la communication avec la partie serveur en se reposant sur AJAX et offre aussi un support de JUnit.

Ce framework propose plusieurs originalités intéressantes :

Le grand avantage de GWT est que l'application web utilisant Ajax et développée avec ce framework ne nécessite essentiellement que des connaissances en Java : quelques rudiments d'HTML et CSS sont nécessaires pour développer des applications GWT mais aucun code JavaScript n'est à écrire.

Une application GWT peut être écrite avec un des IDE Java : ceci rend l'application facilement débogable avec les fonctionnalités de l'IDE.

La version 1.4 de GWT repose sur Java 1.4.

La version 1.5 de GWT repose sur Java 1.5 et offre un support des fonctionnalités de Java 5 (énumérations, generics, ...)

Une application GWT est contenue dans un module. Un module est un ensemble de classes et un fichier de configuration. Un module possède un point d'entrée (entry point) qui correspond à la classe principale qui sera utilisée au lancement de l'application.

Côté serveur, il est possible d'utiliser toutes les technologies capables de traiter des requêtes http (Java, .Net, PHP, ...). Java est particulièrement bien adapté à cette tâche grâce à ses nombreuses API et frameworks disponibles.

Une application de gestion développée avec GWT est donc composée de classes Java :  

Remarque : la partie serveur n'a pas d'obligation à être développée en Java. Elle peut être développée avec d'autres plate-formes mais GWT offre des facilités pour l'utilisation de Java

Lors du déploiement, l'application web sera générée à partir du code Java pour produire les codes HTML et JavaScript requis pour la partie cliente.

Une application GWT peut être exécutée dans deux modes :

 

80.1.1. L'installation de GWT

Il faut télécharger GWT à l'url : http://code.google.com/webtoolkit/download.html

La version utilisée dans ce chapitre est la 1.3.3 sous Windows. Le fichier téléchargé se nomme donc gwt-windows-1.3.3.zip. Il faut décompresser le contenu de ce fichier dans un répertoire du système en utilisant un outil gérant le format zip comme l'utilitaire jar fourni avec le JDK.

 

80.1.2. GWT version 1.6

La version 1.6 de GWT fut publiée en mai 2006. Cette version apporte de nombreuses évolutions notamment :

 

80.1.2.1. La nouvelle structure pour les projets

La structure des répertoires des projets GWT a été adaptée notamment pour respecter le standard des applications web et faciliter la création du packaging de déploiement sous la forme d'une archive war.

Le répertoire cible de génération des livrables se nomme d'ailleurs /war : ce répertoire contiendra le résultat des compilations mais aussi les ressources statiques nécessaires aux modules de l'application. Ainsi certaines adaptations sont nécessaires par rapport à l'organisation des projets des versions antérieures notamment :

Deux nouveaux outils sont proposés pour exploiter cette nouvelle structure de projet :

L'application GWTShell utilisait un serveur Tomcat embarqué. L'application HostedMode utilise un serveur Jetty embarqué.

La partie graphique cliente possède un nouveau bouton « Restart Server » qui permet de redémarrer le serveur Jetty : cela permet de prendre en compte des modifications dans la partie serveur sans avoir à arrêter et relancer l'application graphique cliente comme c'était le cas avec GWTShell.

 

80.1.2.2. Un nouveau système de gestion des événements

Le système de gestion des événements par listener est remplacé par un système de gestion par handler.

Les principales différences entre le deux systèmes sont :

La création de ses propres composants est facilitée car il n'est plus nécessaire de gérer manuellement les listeners. Tous les composants possèdent un objet de type HandlerManager dont le but est de gérer les handlers enregistrés auprès du composant.

La méthode addDomHandler() permet de gérer des événements natifs tels que ClickEvent par exemple : le handler est alors invoqué à l'émission de l'événement.

Exemple :
    Button bouton = new Button("fermer");
   
    bouton.addClickListener(new ClickListener() {
      @Override
      public void onClick(Widget sender) {
        // traitement du clic sur le bouton        

      }
    });

Exemple :
    Button bouton = new Button("fermer");
   
    bouton.addClickHandler(new ClickHandler() {
      @Override
      public void onClick(ClickEvent event) {
        // traitement du clic sur le bouton               

      }      
    });

Certains handlers existants ont dû être adaptés :

Exemple :
    final DisclosurePanel panel = new DisclosurePanel("Cliquez pour ouvrir");
   
    panel.addEventHandler(new DisclosureHandler() {
      public void onClose(DisclosureEvent event) {
        panel.getHeaderTextAccessor().setText("Cliquez pour ouvrir");
      }

      public void onOpen(DisclosureEvent event) {
        panel.getHeaderTextAccessor().setText("Cliquez pour fermer");
      }
    });
    
    panel.add(new Image("images/logo_java.jpg"));
    panel.setWidth("300px");
   
    RootPanel.get("app").add(panel);

Exemple :
    final DisclosurePanel panel = new DisclosurePanel("Cliquez pour ouvrir");
    
    panel.addOpenHandler(new OpenHandler<DisclosurePanel>() {
      @Override
      public void onOpen(OpenEvent<DisclosurePanel> event) {
        panel.getHeaderTextAccessor().setText("Cliquez pour fermer");
      }
    });
    
    panel.addCloseHandler(new CloseHandler<DisclosurePanel>() {
      @Override
      public void onClose(CloseEvent<DisclosurePanel> event) {
        panel.getHeaderTextAccessor().setText("Cliquez pour ouvrir");
      }  
    });
    
    panel.add(new Image("images/logo_java.jpg"));
    panel.setWidth("300px");
   
    RootPanel.get("app").add(panel);

 

80.1.2.3. De nouveaux composants

Les composants DatePicker et DateBox permettent à l'utilisateur de sélectionner une date dans un calendrier.

Le panneau de type LazyPanel permet de retarder la création des objets utiles pour son rendu lorsqu'il sera affiché pour la première fois : ceci peut permettre d'améliorer le temps de démarrage de l'application qui n'est plus obligée d'instancier les composants de tous les éléments de l'application.

 

80.1.3. GWT version 1.7

GWT 1.7 est une mise à jour mineure qui apporte un meilleur support pour les dernières versions des navigateurs (Internet Explorer 8, Firefox 3.5 et Safari 4) et corrige quelques bugs majeurs. Elle a été publiée en juillet 2009.

Cette version est téléchargeable à l'url http://code.google.com/p/google-web-toolkit/downloads/list.

Pour un projet en GWT 1.6, il suffit simplement de le recompiler avec la version 1.7, sans modifier le code, pour que l'application soit compatible avec les nouvelles versions des navigateurs supportés.

 

80.2. La création d'une application

GWT propose plusieurs scripts pour générer des projets GWT composés d'une structure de répertoires et de fichiers fournissant le minimum pour développer un projet.

Pour créer un nouveau projet, il faut créer un nouveau répertoire et utiliser l'application applicationCreator fournie avec GWT. Cet outil permet de créer une petite application d'exemple qui peut facilement servir de base pour le développement d'une application utilisant GWT.

La version Windows de GWT contient un script pour lancer l'application ApplicationCreator. Il suffit d'exécuter ce script avec en paramètre le nom pleinement qualifié de la classe principale de l'application. Le dernier package de cette classe doit se nommer obligatoirement client pour éviter une erreur lors de l'exécution de ApplicationCreator

Résultat :
D:\gwt-windows-1.3.3>ApplicationCreator com.jmdoudoux.testgwt.monapp
'com.jmdoudoux.testgwt.monapp': Please use 'client' as the final package, as in
'com.example.foo.client.MyApp'.
It isn't technically necessary, but this tool enforces the best practice.
Google Web Toolkit 1.3.3
ApplicationCreator [-eclipse projectName] [-out dir] [-overwrite] [-ignore] className

where
  -eclipse    Creates a debug launch config for the named eclipse project
  -out        The directory to write output files into (defaults to current)
  -overwrite  Overwrite any existing files
  -ignore     Ignore any existing files; do not overwrite
and
  className   The fully-qualified name of the application class to create

Par défaut, les fichiers générés le sont dans le répertoire principal. Pour préciser le répertoire à utiliser (celui-ci doit exister), il faut utiliser le paramètre -out.

Résultat :
D:\gwt-windows-1.3.3>mkdir MonApp

D:\gwt-windows-1.3.3>ApplicationCreator -out MonApp com.jmdoudoux.testgwt.client
.MonApp
Created directory MonApp\src
Created directory MonApp\src\com\jmdoudoux\testgwt
Created directory MonApp\src\com\jmdoudoux\testgwt\client
Created directory MonApp\src\com\jmdoudoux\testgwt\public
Created file MonApp\src\com\jmdoudoux\testgwt\MonApp.gwt.xml
Created file MonApp\src\com\jmdoudoux\testgwt\public\MonApp.html
Created file MonApp\src\com\jmdoudoux\testgwt\client\MonApp.java
Created file MonApp\MonApp-shell.cmd
Created file MonApp\MonApp-compile.cmd

Plusieurs répertoires et fichiers sont créés :

Pour exécuter l'application en mode hôte, il suffit donc de lancer le script MonApp-shell.cmd

Résultat :
D:\gwt-windows-1.3.3>cd MonApp

D:\gwt-windows-1.3.3\MonApp>dir
 Le volume dans le lecteur D s'appelle Java
 Le numéro de série du volume est D8C0-0514

 Répertoire de D:\gwt-windows-1.3.3\MonApp

30/07/2007  22:10    <REP>          .
30/07/2007  22:10    <REP>          ..
30/07/2007  22:10               186 MonApp-compile.cmd
30/07/2007  22:10               195 MonApp-shell.cmd
30/07/2007  22:10    <REP>          src
               2 fichier(s)              381 octets
               3 Rép(s)  16 037 978 112 octets libres

D:\gwt-windows-1.3.3\MonApp>MonApp-shell.cmd

Pour vérifier la bonne exécution de l'application, il suffit de cliquer sur le bouton "Click me" pour voir apparaître un message.

 

80.2.1. L'application générée

L'application générée se compose de plusieurs fichiers qui constituent sa base.

Les fichiers sources de l'application sont stockés dans un package qui contient toujours trois sous-répertoires :

Package

Rôle

client

Contient les classes qui composent la partie interface graphique de l'application. Seules les classes de ce package et de ses sous-packages seront compilées en Java

public

Contient les ressources statiques web : pages HTML, images, JavaScript, feuilles de style CSS, ...

server

Contient les classes qui seront exécutées côté serveur


L'application repose sur une page html nommée nom_du_projet.html dans le répertoire public.

Le code de l'application est contenu dans la classe nom_du_projet.java du répertoire client.

Une application GWT est contenue dans un module. La configuration d'un module est le fichier nom_du_projet.gwt.xml.

 

80.2.1.1. Le fichier MonApp.html

Le fichier MonApp.html du sous-répertoire public contient la structure de la page de l'application.

Le fichier html d'une application GWT est généralement très simple : la page html sert d'enveloppe pour recevoir les différents composants graphiques qui seront ajoutés grâce à du code Java.

Exemple :
<html>
<head>
<title>Wrapper HTML for MonApp</title>
<style>
  body,td,a,div,.p{font-family:arial,sans-serif}
  div,td{color:#000000}
  a:link,.w,.w a:link{color:#0000cc}
  a:visited{color:#551a8b}
  a:active{color:#ff0000}
</style>
<meta name='gwt:module' content='com.jmdoudoux.testgwt.MonApp'>

</head>
<body>
<script language="javascript" src="gwt.js"></script>
<iframe id="__gwt_historyFrame" style="width:0;height:0;border:0"></iframe>
<h1>MonApp</h1>
<p>This is an example of a host page for the MonApp application. You
can attach a Web Toolkit module to any HTML page you like, making it
easy to add bits of AJAX functionality to existing pages without
starting from scratch.</p>
<table align=center>
  <tr>
    <td id="slot1"></td>
    <td id="slot2"></td>
  </tr>
</table>
</body>
</html>

Les deux balises <td> possèdent des identifiants distincts : ils définissent des conteneurs dans lesquels les composants vont être ajoutés. Chaque identifiant sera utilisé dans le code Java pour obtenir une référence sur le conteneur.

Le script JavaScript gwt.js est utilisé pour lancer l'application notamment en exécutant la version de l'application dédiée au navigateur utilisé.

L'iframe est utilisé dans le mécanisme de gestion de l'historique de navigation.

 

80.2.1.2. Le fichier MonApp.gwt.xml

Ce fichier contient la définition et la configuration du module notamment :

C'est un fichier XML dont l'extension est .gwt.xml. Le tag racine est le tag <module>

Le tag <inherits> permet de préciser les fonctionnalités de base qui composeront le module.

Le tag <entry-point> permet de préciser la classe pleinement qualifiée qui est le point d'entrée de l'application : l'attribut class permet de préciser le nom de la classe principale de l'application.

Exemple :
<module>

  <!-- Inherit the core Web Toolkit stuff.                  -->
  <inherits name='com.google.gwt.user.User' />

  <!-- Specify the app entry point class.                   -->
  <entry-point class='com.jmdoudoux.testgwt.client.MonApp' />

</module>

 

80.2.1.3. Le fichier MonApp.java

La classe MonApp contient le code de l'application avec notamment :

La méthode onModuleLoad() est le point d'entrée de l'application. Cette méthode contient la définition de l'IHM de l'application.

La mise en oeuvre des gestionnaires d'événements est similaire à celle d'autres framework permettant le développement d'interfaces graphiques tels que AWT, Swing ou SWT. Elle repose sur l'enregistrement de listeners généralement définis sous la forme de classes anonymes.

Exemple :
package com.jmdoudoux.testgwt.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class MonApp implements EntryPoint {

  /**
   * This is the entry point method.
   */
  public void onModuleLoad() {
    final Button button = new Button("Click me");
    final Label label = new Label();

    button.addClickListener(new ClickListener() {
      public void onClick(Widget sender) {
        if (label.getText().equals(""))
          label.setText("Hello World!");
        else
          label.setText("");
      }
    });

    // Assume that the host HTML has elements defined whose
    // IDs are "slot1", "slot2".  In a real app, you probably would not want
    // to hard-code IDs.  Instead, you could, for example, search for all 
    // elements with a particular CSS class and replace them with widgets.
    //
    RootPanel.get("slot1").add(button);
    RootPanel.get("slot2").add(label);
  }
}

Important : les classes de la partie client ne peuvent pas faire référence aux classes de la partie serveur.

 

80.3. Les modes d'exécution

Comme évoqué en introduction, une application GWT peut être exécutée dans deux modes :

 

80.3.1. Le mode hôte (hosted mode)

Dans ce mode, l'application est exécutée de façon hybride sous la forme de code Java exécuté dans un navigateur spécial. Ceci permet notamment l'utilisation du débogueur d'un IDE.

L'environnement d'exécution est composé d'une console, d'un conteneur web (Tomcat ou Jetty selon la version de GWT) et d'un navigateur dédié (Internet Exploreur sous Windows et Firefox sous Linux).

Remarque : le mode hôte n'est disponible que sous Windows et Linux.

Pour exécuter une application dans le mode hôte, il faut exécuter le script dont le nom se compose du nom de l'application et se termine par -shell. Plusieurs options peuvent être fournies à l'environnement d'exécution :

Exemple avec l'option -logLevel ALL

Le mode hôte, facilite grandement l'écriture et la mise au point de l'application en permettant :

L'environnement d'exécution affiche deux fenêtres :

Lorsque l'application est exécutée en mode hôte :

 

80.3.2. Le mode web (web mode)

Dans le mode web, la partie cliente de l'application doit être compilée en JavaScript. Un script de compilation est généré lors de la création de l'application. Le nom de ce script est composé du nom de l'application suivi de « -compile.cmd ».

Le compilateur possède plusieurs options :

Le compilateur génère plusieurs fichiers correspondant à chaque navigateur supporté par le compilateur et éventuellement un pour chaque langue mise en oeuvre pour internationaliser l'application. Ceci permet de réduire la taille du fichier JavaScript de l'application car elle ne contient que du code pour le navigateur et la Locale utilisés.

Résultat :
D:\gwt-windows-1.3.3\MonAppProjet>MonApp-compile.cmd
Output will be written into D:\gwt-windows-1.3.3\MonAppProjet\www\com.jmdoudoux.
testgwt.MonApp
Copying all files found on public path
Compilation succeeded

Le répertoire www est créé : il contient un sous-répertoire qui porte le nom du package principal de l'application. Ce répertoire contient les fichiers générés.

Résultat : le contenu du répertoire de D:\gwt-windows-1.3.3\MonAppProjet\www\com.jmdoudoux.testgwt.MonApp
D:\gwt-windows-1.3.3\MonAppProjet\www\com.jmdoudoux.testgwt.MonApp>dir
 Le volume dans le lecteur D s'appelle Java
 Le numéro de série du volume est D8C0-0514
13/08/2007  23:03    <REP>          .
13/08/2007  23:03    <REP>          ..
13/08/2007  23:03            38 805 0A3A82524C61CDBB6FEA7286E3F85391.cache.html
13/08/2007  23:03               801 0A3A82524C61CDBB6FEA7286E3F85391.cache.xml
13/08/2007  23:03            38 865 4D7E1609F2BC0A4229FFC55F98976B68.cache.html
13/08/2007  23:03               798 4D7E1609F2BC0A4229FFC55F98976B68.cache.xml
13/08/2007  23:03            38 628 921FF51D706A4D67E10268B5DD22D7D7.cache.html
13/08/2007  23:03               801 921FF51D706A4D67E10268B5DD22D7D7.cache.xml
13/08/2007  23:03            38 422 942F11931D582C6DF0166C3EA388BE17.cache.html
13/08/2007  23:03               798 942F11931D582C6DF0166C3EA388BE17.cache.xml
13/08/2007  23:03             2 900 com.jmdoudoux.testgwt.MonApp.nocache.html
13/08/2007  23:03            17 336 gwt.js
13/08/2007  23:03               444 history.html
13/08/2007  23:03               501 MonApp.css
13/08/2007  23:03               512 MonApp.html
13/08/2007  23:03                82 tree_closed.gif
13/08/2007  23:03                78 tree_open.gif
13/08/2007  23:03                61 tree_white.gif
              16 fichier(s)          179 832 octets

Les fichiers .cache.html contiennent le code JavaScript pour chaque navigateur. Le nom du fichier est encrypté. Chacun de ces fichiers possède un fichier avec le même nom et l'extension .cache.xml.

Ces fichiers donnent des informations sur le contenu des fichiers

Exemple : le fichier4D7E1609F2BC0A4229FFC55F98976B68.cache.xml
<?xml version="1.0" encoding="UTF-8"?>
<cache-entry>
        <rebind-decision in="com.google.gwt.user.client.ui.impl.TextBoxImpl" 
          out="com.google.gwt.user.client.ui.impl.TextBoxImpl"/>
        <rebind-decision in="com.google.gwt.user.client.impl.DOMImpl" 
          out="com.google.gwt.user.client.impl.DOMImplMozilla"/>
        <rebind-decision in="com.google.gwt.user.client.impl.HistoryImpl" 
          out="com.google.gwt.user.client.impl.HistoryImplStandard"/>
        <rebind-decision in="com.google.gwt.user.client.ui.impl.FormPanelImpl" 
          out="com.google.gwt.user.client.ui.impl.FormPanelImpl"/>
        <rebind-decision in="com.jmdoudoux.testgwt.client.MonApp" 
          out="com.jmdoudoux.testgwt.client.MonApp"/>
        <rebind-decision in="com.google.gwt.user.client.ui.impl.PopupImpl" 
          out="com.google.gwt.user.client.ui.impl.PopupImpl"/>
</cache-entry>

Ainsi, le fichier 4D7E1609F2BC0A4229FFC55F98976B68 correspond au code pour le navigateur Mozilla.

L'ouverture du fichier MonApp.html dans un navigateur lance l'application

Pour diffuser l'application qui ne contient pas de partie serveur, il suffit de copier les fichiers générés, hormis les fichiers .xml, dans un serveur web.

Pour une application qui contient une partie serveur, il faut packager l'application dans un war. Cette archive doit contenir :

 

80.4. Les éléments de GWT

GWT se compose de plusieurs éléments :

 

80.4.1. Le compilateur

Le compilateur GWT de code Java en JavaScript est encapsulé dans la classe com.google.gwt.dev.GWTCompiler.

Le compilateur traite l'entry point pour chacune de ses dépendances. Le compilateur utilise les fichiers sources mais n'utilise pas les fichiers .class.

Le compilateur des versions antérieures à la version 1.5 de GWT ne supporte que du code source respectant la syntaxe de Java 1.4 : les fonctionnalités de Java 5 ne sont donc pas supportées avant la version 1.5 de GWT. Cette restriction n'est valable que pour le code de la partie cliente qui sera transformé en JavaScript. La partie serveur n'est pas concernée par cette restriction puisqu'elle est compilée en bytecode pour être exécutée dans la JVM du serveur.

Le compilateur génère un fichier JavaScript par navigateur et par langage si l'internationalisation est utilisée dans l'application. Le fichier pour le navigateur concerné sera chargé au lancement de l'application. L'intérêt majeur est de limiter le code JavaScript au navigateur utilisé : ceci évite d'avoir à gérer de nombreuses opérations de tests sur le navigateur comme cela est fréquent dans le code JavaScript.

Cette fonctionnalité est aussi mise en oeuvre pour chaque langue utilisée pour internationaliser l'application. Le code JavaScript ne contient que les libellés pour la langue du fichier.

Le compilateur peut mettre en oeuvre des techniques d'obfuscation du code JavaScript généré afin de le protéger et surtout de réduire sa taille.

Au final, le code contenu dans chaque fichier généré est le plus réduit possible.

 

80.4.2. JRE Emulation Library

Pour utiliser certaines classes de la bibliothèque de base de Java, GWT propose le JRE Emulation Library qui contient les classes fréquemment utilisées dans les applications. Ces classes sont un sous-ensemble de la bibliothèque correspondant à celles qui peuvent être transformées en JavaScript par le compilateur.

Les classes du package java.lang incluses dans le JRE Emulation Library sont :

Boolean

Byte

Character

Class

Double

Float

Integer

Long

Math

Number

Object

Short

String

StringBuffer

System

Throwable

Error

Exception


Il existe aussi des différences entre leur utilisation dans une JVM et dans le JRE Emulation Library. Ces différences sont essentiellement imposées par la conversion du code en JavaScript.

Certaines fonctionnalités de ces classes sont ainsi différentes de leurs homologues de la bibliothèque Java, par exemple :

Attention : JavaScript ne propose pas de support pour les entiers sur 64 bits représentés par une variable de type long en Java. Le compilateur transforme les types long en double. Le fonctionnement de l'application peut donc être différent dans le mode host et web.

La méthode getStackTrace() de la classe Throwable n'est pas utilisable dans le mode web.

Les assertions (mot clé assert) sont ignorées par le compilateur.

JavaScript n'est pas multithread : tout ce qui concerne le multithreading dans le langage Java est donc inutilisable et ignoré par le compilateur.

L'API reflexion permettant une utilisation dynamique des objets n'est pas utilisable. L'API GWT propose uniquement la méthode GWT.getTypeName() : elle renvoie une chaîne de caractères qui correspond au type de l'objet fourni en paramètre.

JavaScript ne propose pas le support pour la finalisation des objets lors de leur traitement par le garbage collector.

JavaScript ne propose pas le support d'une précision constante dans les calculs en virgule flottante. Il n'est donc pas recommandé d'effectuer de tels calculs dans la partie cliente. Des calculs en virgule flottante peuvent être réalisés mais leur précision n'est pas garantie.

La sérialisation proposée par Java n'est pas supportée par GWT qui a son propre mécanisme pour les appels de type RPC vers le serveur.

Pour des raisons de performance, il n'est pas recommandé d'utiliser des objets de type Long, Float ou Double comme clé pour des objets de type Map.

Les classes du package java.lang incluses dans le JRE Emulation Library sont :

AbstractCollection

AbstractList

AbstractMap

AbstractSet

ArrayList

Arrays

Collections

Date

HashMap

Stack

Vector

Collection

Comparator

EventListener

Iterator

List

Map

RandomAccess

Set

   

 

80.4.3. Les modules

La classe qui est le point d'entrée d'un module doit implémenter l'interface EntryPoint. Cette interface définit la méthode void onLoadModule().

Un module contient un fichier descripteur au format XML. Son nom est composé du nom du module suivi de .gwt.xml

Il permet de préciser :

Ce fichier est stocké dans le répertoire contenant la classe qui sert de point d'entrée au module.

Le tag <module> est le tag racine.

Le tag <inherits> permet de préciser un autre module qui sera utilisé. L'attribut name permet de préciser le nom du module.

Le tag <source> permet de préciser le répertoire qui contient des sources à compiler grâce à l'attribut path.

Le tag <stylesheet> permet de préciser une feuille de style CSS. L'attribut src permet de préciser le nom du fichier CSS.

Le tag <servlet> permet de définir une servlet qui sera utilisée pour les communications de type RPC avec le serveur en mode hosted. L'attribut path permet de préciser l'uri de la servlet. L'attribut class permet de préciser le nom pleinement qualifié de la classe qui encapsule la servlet.

 

80.4.4. Les limitations

Le code de l'application est compilée en JavaScript : seules les fonctionnalités compilables en JavaScript peuvent être utilisées. Ainsi par exemple, il n'est pas possible d'utiliser le type primitif long puisque JavaScript ne supporte pas le 64 bits. Cependant le code se compile parfaitement puisque chaque variable de type long est convertie en type double ce qui peut provoquer des effets de bord.

JavaScript n'est pas multithread : il faut en tenir compte lors du développement de l'application.

 

80.5. L'interface graphique des applications GWT

Pour la partie graphique de l'application, GWT propose un ensemble de composants de deux types :

En plus de ces composants proposés en standard par GWT, il est possible de développer ses propres composants.

Il existe plusieurs projets open source qui développent d'autres composants (calendrier, grille, ...) ou des composants qui encapsulent des bibliothèques JavaScript existantes (Scriptaculous, Google Search et Map, ...).

Les composants possèdent une double représentation :

L'organisation des composants n'est pas assurée par des layouts mais par des panneaux qui rendent mieux en HTML. Par exemple :

GWT propose un ensemble complet de composants graphiques (widgets) et panneaux (panels).

L'état de l'interface graphique est maintenu sur le client dans une application GWT.

Exemple :
package com.jmdoudoux.test.gwt.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;

public class MainEntryPoint implements EntryPoint {

  private boolean etat = true;
  
  public MainEntryPoint() {
  }

  public void onModuleLoad() {
    final TextBox text = new TextBox();
    text.setText("AAAAA");
    final Button button = new Button();
    button.setText("Inverser");
    button.addClickListener(new ClickListener() {

      public void onClick(Widget sender) {
        etat = !etat;
        if (etat) {
          text.setText("AAAAA");
        } else {
          text.setText("ZZZZZ");  
        }
      }
    });
    Panel main = new FlowPanel();
    RootPanel.get().add(main);
    main.add(text);
    main.add(button);
  }
}

Un clic sur le bouton "Inverser" inverse les lettres affichées

 

80.6. La personnalisation de l'interface

Comme une application GWT est compilée pour générer une application utilisant le DHTML et JavaScript, la personnalisation de l'interface de l'application repose sur les feuilles de style CSS.

Le rendu des composants d'une application GWT peut donc être assuré par des styles CSS.

La plupart des composants ayant un rendu graphique possèdent une classe de style CSS, par défaut, composée de gwt- suivi du nom du composant (exemple : gwt-Button, gwt-CheckBox, ...).

Il est possible d'utiliser une feuille de style CSS définie dans un fichier stocké dans le sous-répertoire public de l'application.

Exemple : le fichier monstyle.css

Résultat :
root { 
    display: block;
}

.message
{
    color: blue;
    display: block;
    width: 450px;
    padding: 2px 4px;
    margin-top: 3px;
    text-decoration: none;
    text-align: center;
    font-family: Verdana,Arial,Helvetica,sans-serif;
    font-size: 10px;
    border: 1px solid;
    border-color: black;
    ext-decoration: none;
}

.erreur
{
    color: white;
    display: block;
    width: 450px;
    padding: 2px 4px;
    margin-top: 3px;
    text-decoration: none;
    text-align: center;
    font-family: Verdana,Arial,Helvetica,sans-serif;
    font-size: 10px;
    font-weight: bold;
    border: 1px solid;
    border-color: black;
    ext-decoration: none;
    background-color: red;
}

Pour que la feuille de style CSS soit prise en compte par l'application, il faut la déclarer dans le fichier de configuration du module. Cette déclaration se fait à l'aide du tag <stylesheet>. Son attribut src permet de préciser le nom du fichier CSS.

Exemple :
<?xml version="1.0" encoding="UTF-8"?>
<module>
  <inherits name="com.google.gwt.user.User"/>
  <entry-point class="com.jmdoudoux.test.gwt.client.MainEntryPoint"/>
  <stylesheet src="monstyle.css"/>
</module>

Chaque composant possède plusieurs méthodes héritées de la classe UIObject et relatives aux styles CSS :

Exemple :
...
            public void onSuccess(Object result) {
                lblMessage.setStyleName("message");
                lblMessage.setText((String) result);
            }

            public void onFailure(Throwable caught) {
                lblMessage.setStyleName("erreur");
                lblMessage.setText("Echec de la communication");
            }
...

 

80.7. Les composants (widgets)

GWT propose un ensemble complet de composants graphiques de base pour le développement de l'interface graphique d'une application.

Tous les composants héritent de la classe com.google.gwt.user.client.ui.UIObject.

La classe UIObject est la super classe des classes Widget, MenuItem et TreeItem. La classe Widget est la super classe de la quasi-totalité des composants graphiques de GWT.

Composant

Description

Version de GWT

Classe CSS par défaut

Button

Bouton

1.0

gwt-Button

ButtonBase

Classe mère des boutons

   

CheckBox

Case à cocher

1.0

gwt-CheckBox

Composite

Classe qui permet de créer un nouveau composant par assemblage

   

DateBox

Zone de saisie de texte qui ouvre un DatePicker

1.6  

DatePicker

Permet de sélectionner une date

1.6  

FileUpload

Elément HTML de type <input type="file">

1.1  

FocusWidget

Classe mère des composants pouvant avoir le focus

   

Hidden

Champ de type HIDDEN dans un formulaire HTTP

1.2  

HTML

Contient du code HTML

1.0

gwt-HTML

Hyperlink

Hyperlien

1.1

gwt-Hyperlink

Image

Image

1.1

gwt-Image

Label

Zone de texte

 

gwt-Label

ListBox

Liste ou liste déroulante

 

gwt-ListBox

MenuBar

Barre de menus

1.0

gwt-MenuBar

MenuItem

Elément d'une barre de menus

1.0

gwt-MenuItem

PasswordTextBox

Zone de saisie de texte masqué

 

gwt-PasswordTextBox

RadioButton

Bouton radio

 

gwt-RadioButton

RichTextArea

Zone de saisie de texte riche

1.4

 

SuggestBox

Zone de saisie de texte qui suggère des valeurs selon la saisie

1.4

 

TabBar

Une barre d'onglets

1.0

gwt-TabBar
gwt-TabBarFirst
gwt-TabBarRest gwt-TabBarItem
gwt-TabBarItem-selected

TextArea

Zone de saisie de texte multiligne

1.0

gwt-TextArea

TextBox

Zone de saisie de texte monoligne

1.0

gwt-TextBox

TextBoxBase

Classe mère des zones de saisie de texte

   

ToogleButton

     

Tree

Treeview

1.0

gwt-Tree

TreeItem

Elément d'un composant treeview

1.0

gwt-TreeItem,

gwt-TreeItem-selected

UIObject

Classe mère des éléments graphiques

   

Widget

Classe mère des composants

   

 

80.7.1. Les composants pour afficher des éléments

GWT propose plusieurs composants graphiques pour afficher du texte ou des images.

 

80.7.1.1. Le composant Image

La classe Image encapsule une image qui sera affichée. Dans l'arbre Dom, ce composant est un tag <IMG> d'HTML.

Exemple :
    Image image = new Image("images/logo_java.jpg"); 
    
    RootPanel.get("app").add(image);

Il est possible de gérer plusieurs événements émis par ce composant.

La classe Image possède plusieurs listeners utilisables jusqu'à GWT 1.5 : ClickListener, LoadHandler, MouseListener et MouseWheelListener .

Exemple :
    Image image = new Image("images/logo_java.jpg");
    final int largeur = image.getWidth();
    final int hauteur = image.getHeight();
    image.setSize(""+(largeur/2), ""+(hauteur/2));
    image.addClickListener(new ClickListener() {
      @Override
      public void onClick(Widget sender) {
        Image img = (Image) sender;
        if (img.getWidth() == largeur) {
          img.setSize(""+(largeur/2), ""+(hauteur/2));
        } else {
          img.setSize(""+largeur, ""+hauteur);          
        }
      }
    } );
    
    RootPanel.get("app").add(image);

A partir de GWT 1.6, le composant Image propose plusieurs handlers : DomHandler, ClickHandler, ErrorHandler, LoadHandler, MouseDownHandler, MouseMoveHandler, MouseOutHandler, MouseOverHandler, MouseUpHandler et MouseWheelHandler.

Exemple :
    Image image = new Image("images/logo_java.jpg");
    final int largeur = image.getWidth();
    final int hauteur = image.getHeight();
    image.setSize(""+(largeur/2), ""+(hauteur/2));
    image.addClickHandler(new ClickHandler() {
      @Override
      public void onClick(ClickEvent event) {
        Image img = (Image) event.getSource();
        if (img.getWidth() == largeur) {
          img.setSize(""+(largeur/2), ""+(hauteur/2));
        } else {
          img.setSize(""+largeur, ""+hauteur);          
        }        
      }
    });
    
    RootPanel.get("app").add(image);

 

80.7.1.2. Le composant Label

La classe Label encapsule un simple texte qui est affiché.

Exemple :
    Label label = new Label("un libelle");
    
    RootPanel.get("app").add(label);

Il est possible de gérer plusieurs événements émis par ce composant.

La classe Label possède trois listeners utilisables jusqu'à GWT 1.5 : ClickListener, MouseListener et MouseWheelListener .

Exemple :
    Label label = new Label("un libelle");
    ClickListener listener = new ClickListener() {
      @Override
      public void onClick(Widget sender) {
        Window.alert("clic sur le libelle"); 
      }
    };
    
    label.addClickListener(listener);
    
    RootPanel.get("app").add(label);

A partir de GWT 1.6, le composant Label propose plusieurs handlers : ClickHandler, MouseDownHandler, MouseMoveHandler, MouseOutHandler, MouseOverHandler, MouseUpHandler et MouseWheelHandler.

Exemple :
    final Label label = new Label("un libelle");
    MouseOverHandler mouseOverHandler = new MouseOverHandler() {
      @Override
      public void onMouseOver(MouseOverEvent event) {
        label.setStyleName("label-over");
      }
    };
    
    MouseOutHandler mouseOutHandler = new MouseOutHandler() {
      @Override
      public void onMouseOut(MouseOutEvent event) {
        label.removeStyleName("label-over");
      }
    };
    
    label.addMouseOverHandler(mouseOverHandler);
    label.addMouseOutHandler(mouseOutHandler);
   
    RootPanel.get("app").add(label);

 

80.7.1.3. Le composant DialogBox

La classe DialogBox encapsule une fenêtre de type boîte de dialogue.

Exemple :
    private static class MessageInfoBox extends DialogBox {
    
    private MessageInfoBox(String message) {
      setText("Information");
      final DockPanel panel = new DockPanel();
      panel.setVerticalAlignment(HasAlignment.ALIGN_MIDDLE);
      panel.setHorizontalAlignment(HasAlignment.ALIGN_LEFT);
      panel.setStyleName("alignement-gauche");
      panel.add(new Label(message), DockPanel.CENTER);      
      panel.add(new Image("imgages/information.jpg"), DockPanel.WEST);
      
      SimplePanel panelBouton = new SimplePanel();
      panelBouton.setStyleName("alignement-droite");
      Button boutonOk = new Button("OK");
      boutonOk.setWidth("120px");
      boutonOk.addClickHandler(new ClickHandler() {
        public void onClick(ClickEvent event) {
          MessageInfoBox.this.hide();
        }
      });
      panelBouton.add(boutonOk);
      panel.add(panelBouton, DockPanel.SOUTH);
      setWidget(panel);
    }
    
    public static void afficher(String message) {
      new MessageInfoBox(message).center();
    }
  }
  
  public void onModuleLoad() {
    ClickHandler handler = new ClickHandler() {
      @Override
      public void onClick(ClickEvent event) {
        MessageInfoBox.afficher("Ceci est une boite de dialogue d'information");
        }
    };
    Button bouton = new Button("Afficher");
    bouton.addClickHandler(handler);
    RootPanel.get("app").add(bouton);
  }

L'instance du panneau n'a pas besoin d'être rattachée au RootPanel ou à tout autre composant.

Les méthodes center() ou show() permettent d'afficher le panneau et la méthode hide() permet de le masquer.

Contrairement à un PopupPanel, il est possible de préciser la taille d'un DialogBox sans avoir besoin d'ajouter de composants mais en utilisant les méthodes setWitdh() et setHeight().

Avant GWT version 1.6, la gestion des événements se faisait avec des listeners. Un seul listener est défini pour ce composant : PopupListener

A partir de GWT version 1.6, la gestion des événements se fait avec des handlers. Plusieurs handlers sont utilisables avec ce composant : DomHandler et CloseHandler.

 

80.7.2. Les composants cliquables

 

80.7.2.1. La classe Button

La classe Button encapsule un bouton : elle hérite de la classe ButtonBase.

Le style CSS associé est .gwt-Button

Avant GWT version 1.6, la gestion des événements se faisait avec des listeners. Plusieurs listeners sont utilisables avec ce composant : ClickListener, FocusListener et KeyboardListener.

Exemple :
    ClickHandler handler = new ClickHandler() {
      @Override
      public void onClick(ClickEvent event) {
        Window.alert("Bonjour");
      }
    };
    Button bouton= new Button("Afficher");
    bouton.addClickHandler(handler);
    RootPanel.get("app").add(bouton);

A partir de GWT version 1.6, la gestion des événements se fait avec des handlers. Plusieurs handlers sont utilisables avec ce composant : BlurHandler, ClickHandler, DomHandler, FocusHandler, KeyDownHandler, KeyPressHandler, KeyUpHandler, MouseDownHandler, MouseMoveHandler, MouseOutHandler, MouseOverHandler, MouseUpHandler et MouseWheelHandler.

Exemple :
    ClickListener listener = new ClickListener() {
         @Override
         public void onClick(Widget sender) {
                Window.alert("Bonjour");            
        }
    };
    Button bouton = new Button("Afficher", listener);
    RootPanel.get("app").add(bouton);

 

80.7.2.2. La classe PushButton

 

en construction
La suite de cette section sera développée dans une version future de ce document

 

80.7.2.3. La classe ToggleButton

 

en construction
La suite de cette section sera développée dans une version future de ce document

 

80.7.2.4. La classe CheckBox

La classe CheckBox encapsule un bouton de type case à cocher : elle hérite de la classe ButtonBase.

Le style CSS associé est .gwt-CheckBox et .gwt-CheckBox-disabled

Avant GWT version 1.6, la gestion des événements se faisait avec des listeners. Plusieurs listeners sont utilisables avec ce composant : ClickListener, FocusListener, KeyboardListener, MouseListener et MouseWheelListener.

Exemple :
    ClickListener listener = new ClickListener() {
       @Override
       public void onClick(Widget sender) {
        CheckBox cb = (CheckBox) sender;
        if (cb.isChecked()) {
          Window.alert("Bonjour");
        }
      }
    };

    CheckBox bouton = new CheckBox("Afficher");
    bouton.setChecked(false);
    bouton.addClickListener(listener);
    RootPanel.get("app").add(bouton);

A partir de GWT version 1.6, la gestion des événements se fait avec des handlers.Plusieurs handlers sont utilisables avec ce composant : BlurHandler, ClickHandler, DomHandler, FocusHandler, KeyDownHandler, KeyPressHandler, KeyUpHandler, MouseDownHandler, MouseMoveHandler, MouseOutHandler, MouseOverHandler, MouseUpHandler, et MouseWheelHandler

Exemple :
    ClickHandler handler = new ClickHandler() {
         @Override
         public void onClick(ClickEvent event) {
        CheckBox cb = (CheckBox) event.getSource();
        if (cb.getValue()) {
          Window.alert("Bonjour");
        }
      }
    };

    CheckBox bouton = new CheckBox("Afficher");
    bouton.setValue(false);
    bouton.addClickHandler(handler);
    RootPanel.get("app").add(bouton);

 

80.7.2.5. La classe RadioButton

La classe RadioButton encapsule un bouton radio : un seul bouton peut être sélectionné parmi ceux d'un même groupe.

Elle hérite de la classe CheckButton.

Le style CSS associé est .gwt-RadioButton

Pour déterminer ou modifier l'état du bouton, il faut utiliser la propriété value (la propriété checked est deprecated).

Exemple :
    RadioButton rb1 = new RadioButton("valeurs", "Valeur 1");
    RadioButton rb2 = new RadioButton("valeurs", "Valeur 2");
    RadioButton rb3 = new RadioButton("valeurs", "Valeur 3");
    
    rb2.setValue(true);
    VerticalPanel panel = new VerticalPanel();
    panel.add(rb1);
    panel.add(rb2);
    panel.add(rb3);
    
    RootPanel.get("app").add(panel);

Avant GWT version 1.6, la gestion des événements se faisait avec des listeners. Plusieurs listeners sont utilisables avec ce composant : ClickListener, FocusListener, KeyboardListener, MouseListener et MouseWheelListener.

Exemple :
    RadioButton radioButton1 = new RadioButton("valeurs", "Valeur 1");
    RadioButton radioButton2 = new RadioButton("valeurs", "Valeur 2");
    RadioButton radioButton3 = new RadioButton("valeurs", "Valeur 3");
    ClickListener listener = new ClickListener() {
      @Override
      public void onClick(Widget sender) {
         CheckBox checkBox = (CheckBox) sender;
         Window.alert("bouton selectionne = "+checkBox.getText());
      }
    };
    
    radioButton1.addClickListener(listener);
    radioButton2.addClickListener(listener);
    radioButton3.addClickListener(listener);
    
    radioButton2.setValue(true);
    VerticalPanel panel = new VerticalPanel();
    panel.add(radioButton1);
    panel.add(radioButton2);
    panel.add(radioButton3);
    
    RootPanel.get("app").add(panel);

A partir de GWT version 1.6, la gestion des événements se fait avec des handlers. Plusieurs handlers sont utilisables avec ce composant : BlurHandler, ClickHandler, DomHandler, FocusHandler, KeyDownHandler, KeyPressHandler, KeyUpHandler, MouseDownHandler, MouseMoveHandler, MouseOutHandler, MouseOverHandler, MouseUpHandler et MouseWheelHandler

Exemple :
    ClickHandler handler = new ClickHandler() {
      @Override
      public void onClick(ClickEvent event) {
        CheckBox cb = (CheckBox) event.getSource();
        if (cb.getValue()) {
          Window.alert("Bonjour");
        }
      }
    };
    CheckBox bouton = new CheckBox("Afficher");
    bouton.setValue(false);
    bouton.addClickHandler(handler);
    RootPanel.get("app").add(bouton);

 

80.7.2.6. Le composant HyperLink

 

en construction
La suite de cette section sera développée dans une version future de ce document

 

80.7.3. Les composants de saisie de texte

 

80.7.3.1. Le composant TextBoxBase

La classe abstraite TextBoxBase encapsule les fonctionnalités de base d'une zone de saisie de texte.

Elle possède plusieurs méthodes pour agir sur le composant dont les principales sont :

Méthodes

Rôle

void cancelKey()

Supprimer un événement clavier reçu par le composant

int getCursorPos()

Obtenir la position courante du curseur dans le texte saisi

String getSelectedText()

Obtenir le texte sélectionné

int getSelectionLength()

Obtenir le nombre de caractères sélectionnés

String getText()

Obtenir le texte

String getValue()

Obtenir la valeur

boolean isReadOnly()

Déterminer si le composant est en lecture seule

void selectAll()

Sélectionner tout le texte

void setCursorPos(int pos)

Positionner le curseur dans le texte

void setReadOnly(boolean readOnly)

Définir si le composant est en lecture seule

void setSelectionRange(int pos, int length)

Définir la portion de texte sélectionnée

void setText(String text)

Initialiser le texte

void setTextAlignment(TextBoxBase.TextAlignConstant align)

Préciser l'alignement du texte

void setValue(String value)

Mettre à jour la valeur du composant sans émettre d'événements

void setValue(String value, boolean fireEvents)

Mettre à jour la valeur du composant

 

80.7.3.2. Le composant PasswordTextBox

La classe PasswordTextBox encapsule une zone de saisie de texte dont le contenu affiché est masqué. Elle hérite de la classe TextBox.

Exemple :
    PasswordTextBox textBox = new PasswordTextBox();
    textBox.setWidth("200px");
    textBox.setMaxLength(10);
        
    RootPanel.get("app").add(textBox);
    textBox.setFocus(true);

Avant GWT version 1.6, la gestion des événements se faisait avec des listeners. Tous les listeners utilisables avec ce composant sont hérités de sa classe mère TextBox.

A partir de GWT version 1.6, la gestion des événements se fait avec des handlers. Tous les handlers utilisables avec ce composant sont hérités de se classe mère TextBox.

 

80.7.3.3. Le composant TextArea

La classe TextArea encapsule une zone de saisie de texte multiligne. Elle hérite de la classe TextBoxBase.

La méthode setVisibleLines() permet de préciser le nombre de lignes qui seront visibles.

Exemple :
    TextArea textArea = new TextArea();
    textArea.setWidth("200px");
    textArea.setVisibleLines(5);
    RootPanel.get("app").add(textArea);

Avant GWT version 1.6, la gestion des événements se faisait avec des listeners. Plusieurs listeners sont utilisables avec ce composant : ChangeListener, ClickListener, FocusListener, KeyboardListener, MouseListener et MouseWheelListener

A partir de GWT version 1.6, la gestion des événements se fait avec des handlers. Plusieurs handlers sont utilisables avec ce composant : DomHandler, ChangeHandler, ValueChangeHandler, BlurHandler, ClickHandler, FocusHandler, KeyDownHandler, KeyPressHandler, KeyUpHandler, MouseDownHandler, MouseMoveHandler, MouseOutHandler, MouseOverHandler, MouseUpHandler et MouseWheelHandler.

 

80.7.3.4. Le composant TextBox

La classe TextBox encapsule une zone de saisie de texte. Elle hérite de la classe TextBoxBase.

Elle possède plusieurs méthodes pour agir sur le composant dont les principales sont :

Méthodes

Rôle

int getMaxLength()

Obtenir le nombre maximum de caractères saisissables

void setMaxLength(int length)

Limiter le nombre de caractères saisissables


Exemple :
    TextBox textBox = new TextBox();
    textBox.setWidth("200px");
    textBox.setText("mon texte");
    textBox.setMaxLength(50);
    
    RootPanel.get("app").add(textBox);
    textBox.setFocus(true);
    textBox.setCursorPos(4);

Avant GWT version 1.6, la gestion des événements se faisait avec des listeners. Plusieurs listeners sont utilisables avec ce composant : ChangeListener, ClickListener, FocusListener, KeyboardListener, MouseListener et MouseWheelListener

Exemple :
    TextBox textBox = new TextBox();
    textBox.setWidth("200px");
    textBox.setMaxLength(10);
    
    textBox.addKeyboardListener(new KeyboardListener() {
      @Override
      public void onKeyDown(Widget sender, char keyCode, int modifiers) {
      }

      @Override
      public void onKeyPress(Widget sender, char keyCode, int modifiers) {
        if (!Character.isDigit(keyCode)) {
          ((TextBox) sender).cancelKey();
        }
      }

      @Override
      public void onKeyUp(Widget sender, char keyCode, int modifiers) {
      }
      
    });
    
    RootPanel.get("app").add(textBox);
    textBox.setFocus(true);

A partir de GWT version 1.6, la gestion des événements se fait avec des handlers. Plusieurs handlers sont utilisables avec ce composant : DomHandler, ChangeHandler, ValueChangeHandler, BlurHandler, ClickHandler, FocusHandler, KeyDownHandler, KeyPressHandler, KeyUpHandler, MouseDownHandler, MouseMoveHandler, MouseOutHandler, MouseOverHandler, MouseUpHandler et MouseWheelHandler.

Exemple :
    TextBox textBox = new TextBox();
    textBox.setWidth("200px");
    textBox.setMaxLength(10);
    
    textBox.addKeyPressHandler(new KeyPressHandler() {
      @Override
      public void onKeyPress(KeyPressEvent event) {
        if (!Character.isDigit(event.getCharCode())) {
          ((TextBox) event.getSource()).cancelKey();
        }
      }
    });
    
    RootPanel.get("app").add(textBox);
    textBox.setFocus(true) ;

 

80.7.3.5. Le composant RichTextArea

La classe RichTextArea encapsule un composant qui permet à un utilisateur de saisir du texte avec un contenu formaté en HTML.

Le composant RichTextArea est un composant évolué et tout à la fois basique : il permet le formatage de textes riches par l'utilisateur et propose des objets pour formater le contenu texte mais il ne propose pas d'ensemble de boutons pour faciliter la mise en oeuvre de ces fonctionnalités de formatage.

Selon le navigateur utilisé par l'utilisateur, les fonctionnalités utilisables avec ce composant varient. Ces fonctionnalités sont réparties en trois catégories : Aucune (none), Basique (basic) et Etendue (extended).

Chaque catégorie est encapsulée dans un objet de formatage :

Pour déterminer si la catégorie est supportée par le navigateur, il faut vérifier qu'il est possible d'obtenir une instance de l'objet correspondant et utiliser respectivement les méthodes getBasicFormatter() et getExtendedFormatter().

La classe RichTextArea.BasicFormatter propose des méthodes pour des fonctionnalités de formatage basiques, notamment :

Méthode

Rôle

String getBackColor()

Obtenir la couleur de fond

String getForeColor()

Obtenir la couleur

boolean isBold()

Indiquer si la zone est en gras

boolean isItalic()

Indiquer si la zone est en italique

boolean isUnderlined()

Indiquer si la zone est soulignée

void selectAll()

Sélectionner tout le texte

void setBackColor(String color)

Modifier la couleur de fond

void setFontName(String name)

Modifier la police de caractères

void setFontSize(RichTextArea.FontSize fontSize)

Modifier la taille de la police de caractères

void setForeColor(String color)

Modifier la couleur

void setJustification(RichTextArea.Justification justification)

Modifier l'alignement

void toggleBold()

Basculer la zone en gras

void toggleItalic()

Basculer la zone en italique

void toggleUnderline()

Basculer la zone en souligné


Toutes ces méthodes agissent sur la sélection courante ou, à défaut, sur le mot sur lequel le curseur est positionné.

La classe RichTextArea.ExtendedFormatter propose des méthodes pour des fonctionnalités de formatage avancées, notamment :

Méthode

Rôle

void createLink(String url)

Créer un hyperlien sur la zone vers l'url fournie en paramètre

void insertHorizontalRule()

Insérer une barre de séparation

void insertImage(String url)

Insérer une image

void insertOrderedList()

Débuter une liste ordonnée

void insertUnorderedList()

Débuter une liste avec puces

boolean isStrikethrough()

Indique si la zone est barrée

void leftIndent()

Indenter la zone

void removeFormat()

Supprimer tout le formatage de la zone.

void removeLink()

Supprimer l'hyperlien de la zone

void toggleStrikethrough()

Basculer la zone en barré


Exemple :
    VerticalPanel panel = new VerticalPanel();
    final RichTextArea richTextBox = new RichTextArea();
    final TextArea textArea = new TextArea();
    
    final ExtendedFormatter ef = richTextBox.getExtendedFormatter();
    final BasicFormatter bf = richTextBox.getBasicFormatter();
    richTextBox.setWidth("400px");
    richTextBox.setHTML("<h1>Titre</H1><p>Voici
le <b>contenu</b> du paragraphe.</p>");
    
    textArea.setWidth("400px");
    textArea.setVisibleLines(5);
    
    HorizontalPanel boutons = new HorizontalPanel();
    panel.add(boutons);
    if (bf != null)
    {
        // ajout des boutons de fomatage basique
        boutons.add(new Button("Gras", new ClickListener()
        {
            public void onClick(Widget sender)
            {
                bf.toggleBold();
            }
        }));
        
        // ajout des boutons de fomatage etendu
        if (ef != null)
        {
           boutons.add(new Button("Barre", new ClickListener() {
                public void onClick(Widget sender)
                {
                    ef.toggleStrikethrough();
                }
            }));
        }
    }
   
    boutons.add(new Button("Text", new ClickListener()
    {
        public void onClick(Widget sender)
        {
            textArea.setText(richTextBox.getText());
        }
    }));
    boutons.add(new Button("Html", new ClickListener()
    {
        public void onClick(Widget sender)
        {
            textArea.setText(richTextBox.getHTML());
        }
    }));
   
    panel.add(richTextBox);    
    panel.add(textArea);    
    
    RootPanel.get("app").add(panel);
    richTextBox.setFocus(true);

Avant GWT version 1.6, la gestion des événements se faisait avec des listeners. Plusieurs listeners sont utilisables avec ce composant : ClickListener, FocusListener, KeyboardListener et MouseWheelListener

A partir de GWT version 1.6, la gestion des événements se fait avec des handlers. Plusieurs handlers sont utilisables avec ce composant : DomHandler, BlurHandler, ClickHandler, FocusHandler, KeyDownHandler, KeyPressHandler, KeyUpHandler, MouseDownHandler, MouseMoveHandler, MouseOutHandler, MouseOverHandler, MouseUpHandler et MouseWheelHandler

 

80.7.4. Les composants de sélection de données

 

80.7.4.1. Le composant ListBox

La classe ListBox encapsule une liste ou une liste déroulante qui permet à l'utilisateur de choisir un ou plusieurs éléments.

La classe ListBox possède plusieurs constructeurs notamment :

Constructeur

Rôle

ListBox()

Constructeur par défaut

ListBox(boolean isMultipleSelect)

Instancier un composant qui autorisera la sélection multiple d'éléments


La classe ListBox possède plusieurs méthodes notamment :

Méthode

Rôle

void addItem(String item)

Ajouter un élément

void addItem(String item, String value)

Ajouter un élément en précisant sa valeur

void clear()

Supprimer tous les éléments

int getItemCount()

Obtenir le nombre d'éléments de la ListBox

String getItemText(int index)

Obtenir le texte de l'élément dont l'index est fourni en paramètre

int getSelectedIndex()

Obtenir l'index de l'élément sélectionné

String getValue(int index)

Obtenir la valeur de l'élément dont l'index est fourni en paramètre

int getVisibleItemCount()

Obtenir le nombre d'éléments visibles

void insertItem(String item, int index)

Insérer un élément à l'index fourni

void insertItem(String item, String value, int index)

Insérer un élément à l'index fourni en précisant la valeur

boolean isItemSelected(int index)

Déterminer si un élément est sélectionné

boolean isMultipleSelect()

Déterminer si la sélection multiple est possible

void removeItem(int index)

Supprimer l'élément dont l'index est fourni en paramètre

void setItemSelected(int index, boolean selected)

Sélectionner ou non l'élément dont l'index est fourni en paramètre

void setItemText(int index, String text)

Modifier le texte de l'élément dont l'index est fourni en paramètre

void setMultipleSelect(boolean multiple)

Activer ou non la sélection multiple. Deprecated : il faut utiliser le constructeur ListBox(boolean)

void setSelectedIndex(int index)

Modifier l'index correspondant à l'élément sélectionné

void setValue(int index, String value)

Modifier la valeur de l'élément dont l'index est fourni en paramètre

void setVisibleItemCount(int visibleItems)

Modifier le nombre d'éléments affichés


L'affichage du composant se fait par défaut sous la forme d'une liste déroulante.

Exemple :
    ListBox listBox = new ListBox();
    
    for (int i=1; i <10 ; i++) {
      listBox.addItem("element "+i); 
    }
   
    RootPanel.get("app").add(listBox);

La méthode setVisibleItemCount() permet de préciser le nombre d'éléments de la liste qui seront affichés :

Exemple :
    ListBox listBox = new ListBox();
    
    for (int i=1; i <10 ; i++) {
      listBox.addItem("element "+i); 
    }
    listBox.setVisibleItemCount(5);
    
    RootPanel.get("app").add(listBox);

Il est possible de gérer plusieurs événements émis par ce composant.

La classe ListBox possède plusieurs listeners utilisables jusqu'à GWT 1.5 : ChangeListener, ClickListener, MouseListener et MouseWheelListener.

Exemple :
    ListBox listBox = new ListBox();
    
    for (int i=1; i <10 ; i++) {
      listBox.addItem("element "+i, ""+i);  
    }
    listBox.addChangeListener(new ChangeListener(){
      @Override
      public void onChange(Widget sender) {
        ListBox list = (ListBox) sender;
        int index = list.getSelectedIndex();
        Window.alert("element selectionne, index="+index+", texte="+list.getItemText(index)+",
valeur="+list.getValue(index));
      }  
    } ); 
    
    listBox.setVisibleItemCount(5);
    RootPanel.get("app").add(listBox);

A partir de GWT 1.6, le composant ListBox propose plusieurs handlers : ClickHandler, MouseDownHandler, MouseMoveHandler, MouseOutHandler, MouseOverHandler, MouseUpHandler et MouseWheelHandler.

Exemple :
    ListBox listBox = new ListBox();
    
    for (int i=1; i <10 ; i++) {
      listBox.addItem("element "+i, ""+i);  
    }
    listBox.addChangeHandler(new ChangeHandler(){
      @Override
      public void onChange(ChangeEvent event) {
        ListBox list = (ListBox) event.getSource();
        int index = list.getSelectedIndex();
        Window.alert("element selectionne,
index="+index+", texte="+list.getItemText(index)+",
valeur="+list.getValue(index));
      }  
    } ); 
    
    listBox.setVisibleItemCount(5);
    RootPanel.get("app").add(listBox);

 

80.7.4.2. Le composant SuggestBox

La classe SuggestBox encapsule une zone de texte qui permet de sélectionner des suggestions commençant par le texte saisi.

La classe abstraite SuggestOracle encapsule les suggestions liées à une requête.

L'implémentation par défaut est la classe MultiWordSuggestOracle qui recherche les suggestions parmi celles dont au moins un mot commence par le motif de recherche. La recherche n'est pas sensible à la casse et les suggestions retournées sont triées par ordre alphabétique avec le motif recherché mis en évidence.

La méthode add() permet d'ajouter une suggestion. La méthode addAll() permet d'ajouter plusieurs suggestions et la méthode clear() permet de supprimer toutes les suggestions.

Exemple :
    VerticalPanel panel = new VerticalPanel();
    MultiWordSuggestOracle oracle = new MultiWordSuggestOracle();
    oracle.add("Peche fruitee");
    oracle.add("Abricot");
    oracle.add("Banane");
    oracle.add("Fraise");
    oracle.add("Framboise");
    oracle.add("Pomme");
    oracle.add("Poire");
    
    SuggestBox suggestbox = new SuggestBox(oracle);
    suggestbox.setAnimationEnabled(true);
    panel.add(new Label("Fruit : "));
    panel.add(suggestbox);
    RootPanel.get("app").add(panel);
    
    suggestbox.setFocus(true);

Les principaux styles CSS associés sont :

Style

Rôle

.gwt-SuggestBox

Apparence de la zone de saisie

.gwt-SuggestBoxPopup

Apparence de la popup affichant les suggestions

.gwt-SuggestBoxPopup .item

Apparence d'une suggestion

.gwt-SuggestBoxPopup .item-selected

Apparence de la suggestion sélectionnée


Résultat :
/* format de la zone de saisie */
.gwt-SuggestBox 
{
border                 :    1px solid #000;
text-align             :    left;
width                  :    200px;
}

/* format de la popup affichant les suggestions */
.gwt-SuggestBoxPopup 
{
cursor                 :    pointer;
border                 :    1px solid #666;
border-top             :    0;
background-color       :    #fff;
}

/* format d'une suggestion */
.gwt-SuggestBoxPopup .item 
{
text-align             :    center;
border                 :    1px dotted #bbb;
width                  :    200px;
}

/* format de la suggestion selectionnee */
.gwt-SuggestBoxPopup .item-selected 
{
border                       :    1px solid #f00;
}

Avant GWT version 1.6, la gestion des événements se faisait avec des listeners. Plusieurs listeners sont utilisables avec ce composant : ChangeListener, ClickListener, FocusListener et KeyboardListener.

A partir de GWT version 1.6, la gestion des événements se fait avec des handlers. Plusieurs handlers sont utilisables avec ce composant : KeyDownHandler, KeyPressHandler, KeyUpHandler, SelectionHandler, et ValueChangeHandler.

Il est aussi possible d'utiliser plusieurs handlers sur la zone de texte associée au composant dont l'instance est obtenue en invoquant la méthode getTextBox() : ChangeHandler et ClickHandler.

 

80.7.4.3. Le composant DateBox

 

en construction
La suite de cette section sera développée dans une version future de ce document

 

80.7.4.4. Le composant DatePicker

 

en construction
La suite de cette section sera développée dans une version future de ce document

 

80.7.5. Les composants HTML

 

en construction
La suite de cette section sera développée dans une version future de ce document

 

80.7.5.1. Le composant Frame

 

en construction
La suite de cette section sera développée dans une version future de ce document

 

80.7.5.2. Le composant HTML

Le composant HTML permet d'afficher un contenu HTML dans l'application. Il propose plusieurs constructeurs dont un qui permet de fournir le code HTML qui sera affiché.

Exemple :
    HTML widget = new HTML( 
      "<div id='panneau' style='background-color: yellow; border:"+
      "1px solid black; width: 200px; text-align: center;'>"+ 
      "Mon Message d'avertissement</div>"); 
    RootPanel.get("app").add(widget);

La méthode setHTML() permet de modifier le contenu du code HTML affiché par le composant.

Il est possible de gérer plusieurs événements émis par ce composant.

La classe HTML possède plusieurs listeners utilisables jusqu'à GWT 1.5 : ClickListener, MouseListener et MouseWheelListener.

A partir de GWT 1.6, le composant HTML propose plusieurs handlers : DomHandler, ClickHandler, ErrorHandler, LoadHandler MouseDownHandler, MouseMoveHandler, MouseOutHandler, MouseOverHandler, MouseUpHandler et MouseWheelHandler.

 

80.7.5.3. FileUpload

 

en construction
La suite de cette section sera développée dans une version future de ce document

 

80.7.5.4. Hidden

La classe Hidden encapsule un champ invisile d'un formulaire HTML.

La classe Hidden possède plusieurs constructeurs notamment :

Constructeur

Rôle

Hidden()

Constructeur par défaut

Hidden(String name)

Constructeur qui attend le nom du champ en paramètre

Hidden(String name, String value)

Constructeur qui attend le nom et la valeur du champ en paramètres


Elle possède plusieurs méthodes qui permettent de manipuler les données de la classe, notamment :

Méthode

Rôle

String getDefaultValue()

Obtenir la valeur par défaut du champ

String getName()

Obtenir le nom du champ

String getValue()

Obtenir la valeur du champ

void setDefaultValue(String defaultValue)

Modifier la valeur par défaut du champ

void setName(name)

Modifier la nom du champ

void setValue(String value)

Modifier la valeur du champ


Ce composant n'ayant pas de rendu graphique, il ne possède aucun événement.

 

80.7.6. Le composant Tree

Le composant Tree encapsule l'affichage de données sous une forme arborescente. Chaque élément de l'arborescence qui possède au moins un élément fils peut être plié ou déplié pour faire apparaitre ou non les éléments de sa sous-arborescence.

La classe Tree propose plusieurs méthodes notamment :

Méthode

Rôle

void add(Widget widget)

Ajouter un élément affichant le composant fourni en paramètre à la racine de l'arborescence

TreeItem addItem(String itemText)

Ajouter un élément affichant un texte

void addItem(TreeItem item)

Ajouter un élément à la racine de l'arborescence

TreeItem addItem(Widget widget)

Ajouter un élément qui va afficher le composant fourni en paramètre

void clear()

Supprimer tous les éléments

void ensureSelectedItemVisible()

Rendre visible l'élément sélectionné, au besoin en dépliant ses parents

TreeItem getItem(int index)

Obtenir l'élément dont l'index est fourni

int getItemCount()

Obtenir le nombre d'éléments

TreeItem getSelectedItem()

Obtenir l'élément sélectionné

boolean isAnimationEnabled()

Déterminer si l'animation est activée lorsqu'un élément est replié ou déplié

java.util.Iterator<Widget> iterator()

Obtenir un iterator sur les composants

void removeItem(TreeItem item)

Supprimer un élément de la racine

void removeItems()

Supprimer tous les éléments de la racine

void setAnimationEnabled(boolean enable)

Activer ou non l'animation lorsqu'une branche est pliée ou dépliée

void setSelectedItem(TreeItem item)

Sélectionner un élément

java.util.Iterator<TreeItem> treeItemIterator()

Obtenir un itérateur sur les éléments de l'arborescence


La classe Tree est un conteneur pour des éléments de type TreeItem

Le classe TreeItem encapsule un élément d'un composant Tree. Elle propose plusieurs méthodes notamment :

Méthode

Rôle

TreeItem addItem(String itemText)

Ajouter un élément affichant le texte fourni en paramètre

void addItem(TreeItem item)

Ajouter un élément fils

TreeItem addItem(Widget widget)

Ajouter un composant comme élément fils

TreeItem getChild(int index)

Obtenir l'élément fils dont l'index est fourni en paramètre

int getChildCount()

Obtenir le nombre d'éléments fils

int getChildIndex(TreeItem child)

Obtenir l'index d'un élément fils

TreeItem getParentItem()

Obtenir l'élément père

boolean getState()

Déterminer si les éléments fils sont affichés ou non

String getText()

Obtenir le texte de l'élément

Tree getTree()

Obtenir le composant Tree encapsulant toute l'arborescence

Object getUserObject()

Obtenir un objet associé à l'élément

Widget getWidget()

Obtenir le composant associé à l'élément

boolean isSelected()

Déterminer si l'élément est sélectionné ou non

void remove()

Retirer l'élément de l'arborescence

void removeItem(TreeItem item)

Retirer l'élément fils

void removeItems()

Retirer tous les éléments fils

void setSelected(boolean selected)

Sélectionner ou non l'élément

void setState(boolean open)

Afficher ou non les éléments fils

void setText(String text)

Définir le texte de l'élément

void setUserObject(Object userObj)

Associer un objet à l'élément

void setWidget(Widget newWidget)

Définir le composant de l'élément


Les objets de type TreeItem ne peuvent être attachés qu'à un composant de type Tree.

Exemple :
    Tree tree = new Tree();
    tree.setAnimationEnabled(false);
        
    TreeItem element1 = new TreeItem("Element 1");
    element1.addItem("Element 1-1");
    element1.addItem("Element 1-2");
    element1.addItem("Element 1-3");
    tree.addItem(element1);
    TreeItem sousElement1_3 = new TreeItem("Element 1-3");
    sousElement1_3.addItem("Element 1-3-1");
    sousElement1_3.addItem("Element 1-3-2");
    TreeItem element2 = new TreeItem("Element 2");
    element2.addItem("Element 2-1");
    element2.addItem("Element 2-2");
    TreeItem element2_3 = element2.addItem("Element 2-3");
    tree.addItem(element2);
    element1.addItem(sousElement1_3);
    
    RootPanel.get("app").add(tree);

Il est possible d'afficher un composant dans un élément.

Exemple :
    Tree tree = new Tree();
    tree.setAnimationEnabled(false);
        
    TreeItem element1 = new TreeItem("Element 1");
    element1.addItem("Element 1-1");
    element1.addItem("Element 1-2");
    element1.addItem("Element 1-3");
    tree.addItem(element1);
    TreeItem sousElement1_3 = new TreeItem("Element 1-3");
    sousElement1_3.addItem(new CheckBox("Element 1-3-1"));
    sousElement1_3.addItem(new CheckBox("Element 1-3-2"));
    TextBox element1_3_3 = new TextBox();
    element1_3_3.setText("Element 1-3-3");
    sousElement1_3.addItem(element1_3_3);
    
    element1.addItem(sousElement1_3);
    
    RootPanel.get("app").add(tree);

Par défaut, le composant Tree ne possède pas de scrollbars : il s'agrandit au fur et à mesure des éléments dépliés. Pour limiter la taille du composant et afficher une scrollbar, il faut l'encapsuler dans un panneau de type ScrollPanel.

Exemple :
    Tree tree = new Tree();
    tree.setAnimationEnabled(false);
        
    TreeItem element1 = new TreeItem("Element 1");
    element1.addItem("Element 1-1");
    element1.addItem("Element 1-2");
    element1.addItem("Element 1-3");
    tree.addItem(element1);
    TreeItem sousElement1_3 = new TreeItem("Element 1-3");
    sousElement1_3.addItem("Element 1-3-1");
    sousElement1_3.addItem("Element 1-3-2");
    TreeItem element2 = new TreeItem("Element 2");
    element2.addItem("Element 2-1");
    element2.addItem("Element 2-2");
    TreeItem element2_3 = element2.addItem("Element 2-3");
    tree.addItem(element2);
    element1.addItem(sousElement1_3);
    ScrollPanel scrollPanel = new ScrollPanel(tree);
    scrollPanel.setWidth("250px");
    scrollPanel.setHeight("130px");
    
    RootPanel.get("app").add(scrollPanel);
    tree.setSelectedItem(element2_3);
    tree.ensureSelectedItemVisible();

Avant GWT version 1.6, la gestion des événements se faisait avec des listeners. Plusieurs listeners sont utilisables avec ce composant : FocusListener, KeyboardListener, MouseListener et TreeListener .

Exemple :
        Tree tree = new Tree();
    tree.setAnimationEnabled(false);
        
    TreeItem element1 = new TreeItem("Element 1");
    element1.addItem("Element 1-1");
    element1.addItem("Element 1-2");
    element1.addItem("Element 1-3");
    tree.addItem(element1);
    TreeItem sousElement1_3 = new TreeItem("Element 1-3");
    sousElement1_3.addItem(new CheckBox("Element 1-3-1"));
    sousElement1_3.addItem(new CheckBox("Element 1-3-2"));
    TextBox element1_3_3 = new TextBox();
    element1_3_3.setText("Element 1-3-3");
    sousElement1_3.addItem(element1_3_3);
    
    element1.addItem(sousElement1_3);
    tree.addTreeListener(new TreeListener() {
      @Override
      public void onTreeItemSelected(TreeItem item) {
        Window.alert("Selection : "+item.getText());
      }

      @Override
      public void onTreeItemStateChanged(TreeItem item) {
        if (item.getState()) {
          Window.alert("Affichage noeud fils : "+item.getText());
        } else {
          Window.alert("Masquage noeud fils : "+item.getText());        
        }
      }
    });
    
    RootPanel.get("app").add(tree);

A partir de GWT version 1.6, la gestion des événements se fait avec des handlers. Plusieurs handlers sont utilisables avec ce composant : DomHandler, BlurHandler, CloseHandler, FocusHandler, KeyDownHandler, KeyPressHandler, KeyUpHandler, MouseDownHandler, MouseMoveHandler, MouseOutHandler, MouseOverHandler, MouseUpHandler, MouseWheelHandler, OpenHandler et SelectionHandler.

Exemple :
    Tree tree = new Tree();

    tree.setAnimationEnabled(false);
    TreeItem element1 = new TreeItem("Element 1");
    element1.addItem("Element 1-1");
    element1.addItem("Element 1-2");
    element1.addItem("Element 1-3");
    tree.addItem(element1);

    TreeItem sousElement1_3 = new TreeItem("Element 1-3");
    sousElement1_3.addItem(new CheckBox("Element 1-3-1"));
    sousElement1_3.addItem(new CheckBox("Element 1-3-2"));
    TextBox element1_3_3 = new TextBox();
    element1_3_3.setText("Element 1-3-3");
    sousElement1_3.addItem(element1_3_3);
    element1.addItem(sousElement1_3);

    tree.addSelectionHandler(new SelectionHandler<TreeItem>() {
      @Override
      public void onSelection(SelectionEvent<TreeItem> event) {
        Window.alert("Selection : " + event.getSelectedItem().getText());
      }
    });

    tree.addOpenHandler(new OpenHandler<TreeItem>() {
      @Override
      public void onOpen(OpenEvent<TreeItem> event) {
        Window.alert("Affichage noeud fils : "
            + event.getTarget().getText());
      }
    });

    tree.addCloseHandler(new CloseHandler<TreeItem>() {
      @Override
      public void onClose(CloseEvent<TreeItem> event) {
        Window.alert("Masquage noeud fils : "
            + event.getTarget().getText());
      }
    });

    RootPanel.get("app").add(tree);

 

80.7.7. Les menus

La classe MenuBar encapsule un menu et les éléments qui le composent.

Exemple :
package com.jmdoudoux.testgwt.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.MenuBar;
import com.google.gwt.user.client.ui.RootPanel;

public class MonApp implements EntryPoint {

  public void onModuleLoad() {
    MenuBar menu = new MenuBar();
    MenuBar menu1 = new MenuBar(true);
    MenuBar menu2 = new MenuBar(true);

    menu2.addItem("menu2_1", new MonMenuCommand());
    menu2.addItem("menu2_2", new MonMenuCommand());
    menu2.addItem("menu2_3", new MonMenuCommand());

    menu1.addItem("menu1_1", new MonMenuCommand());
    menu1.addItem("menu1_2", new MonMenuCommand());

    menu.addItem("menu1", menu1);
    menu.addItem("menu2", menu2);
    menu.setAutoOpen(true);
    RootPanel.get("menu").add(menu);
    
    menu1.addStyleName("submenu");
    menu2.addStyleName("submenu");
  }

  public class MonMenuCommand implements Command {
    public void execute() {
      Window.alert("Element du menu clické");
    }
  }
}

Le rendu du menu est assuré par des styles CSS. Le plus simple est de les définir dans un fichier .css

Résultat :
body {
   margin: 0;
   padding: 0;
   background: #ffffff;
}
                       
.gwt-MenuBar {
   background: #a0a0a0;
   border: 1px solid #3f3f3f;
   cursor: pointer;
}

.gwt-MenuBar .gwt-MenuItem {
   font-family: Arial, sans-serif;
   font-size: 12px;
   color: #ffffff;
   font-weight: bold;
   padding-right: 10px;
   
}

.gwt-MenuBar .gwt-MenuItem-selected { 
   font-family: Arial, sans-serif;
   font-size: 12px;
   color: #ffffff;
   font-weight: bold;
   padding-right: 10px;
}

Ce fichier doit être déclaré dans le fichier de configuration de l'application grâce au tag <stylesheet>. L'attribut src permet de préciser le fichier.

Exemple :
<module>

    <!-- Inherit the core Web Toolkit stuff.                  -->
    <inherits name='com.google.gwt.user.User' />

    <stylesheet src="MonApp.css" />

    <!-- Specify the app entry point class.                   -->
    <entry-point class='com.jmdoudoux.testgwt.client.MonApp' />

</module> 

Enfin, un calque nommé menu est défini dans le fichier html de la page.

Exemple :
<html>
<head>
<title>Test de Menu</title>
<style>
  body,td,a,div,.p{font-family:arial,sans-serif}
  div,td{color:#000000}
  a:link,.w,.w a:link{color:#0000cc}
  a:visited{color:#551a8b}
  a:active{color:#ff0000}
</style>

<meta name='gwt:module' content='com.jmdoudoux.testgwt.MonApp'>
</head>
<body>
  <div id="menu"></div>
  <script language="javascript" src="gwt.js"></script>
  <iframe id="__gwt_historyFrame" style="width:0;height:0;border:0"></iframe>
  <h1>Test de menu</h1>
</body>
</html>

Résultat :

 

80.7.8. Le composant TabBar

La classe TabBar encapsule une barre d'onglets. Ce composant est essentiellement utilisé dans un panneau TabPanel

Exemple :
    VerticalPanel panel = new VerticalPanel();
    final TabBar tabBar = new TabBar();
    final Label label = new Label();
    
    tabBar.addTab("Onglet 1");
    tabBar.addTab("Onglet 2");
    tabBar.addTab("Onglet 3");
    tabBar.addTab("Onglet 4");
    tabBar.addTabListener(new TabListener() {
      @Override
      public boolean onBeforeTabSelected(SourcesTabEvents sender, int tabIndex) {
        return true;
      }
      
      @Override
      public void onTabSelected(SourcesTabEvents sender, int tabIndex) {
        int selectionne = tabBar.getSelectedTab();
        
        if (selectionne != 0) {
          tabBar.setTabEnabled(3, true);
        }
        
        if (tabIndex == 0) {
          tabBar.setTabEnabled(3, false);         
        }
        
        label.setText("Selection de l'onglet : "+tabBar.getTabHTML(tabIndex));
      }
    });
   
    panel.add(tabBar);
    panel.add(label);
    tabBar.selectTab(0);
    
    RootPanel.get("app").add(panel);

Avant GWT version 1.6, la gestion des événements se faisait avec des listeners. Un seul listener est utilisable avec ce composant : TabListener.

Exemple :
    VerticalPanel panel = new VerticalPanel();
    final TabBar tabBar = new TabBar();
    final Label label = new Label();
    
    tabBar.addTab("Onglet 1");
    tabBar.addTab("Onglet 2");
    tabBar.addTab("Onglet 3");
    tabBar.addTab("Onglet 4");
    tabBar.addTabListener(new TabListener() {
      @Override
      public boolean onBeforeTabSelected(
         SourcesTabEvents sender, int tabIndex)
      {
        return true;
      }
      
      @Override
      public void onTabSelected(SourcesTabEvents sender, int tabIndex)
      {
        int selectionne = tabBar.getSelectedTab();
        
        if (selectionne != 0) {
          tabBar.setTabEnabled(3, true);
        }
        
        if (tabIndex == 0) {
          tabBar.setTabEnabled(3, false);         
        }
        
        label.setText("Selection de l'onglet : "+tabBar.getTabHTML(tabIndex));
      }
    });
    panel.add(tabBar);
    panel.add(label);
    tabBar.selectTab(0);
    
    RootPanel.get("app").add(panel);

A partir de GWT version 1.6, la gestion des événements se fait avec des handlers. Plusieurs handlers sont utilisables avec ce composant : SelectionBeforeHandler et SelectionHandler.

Exemple :
    VerticalPanel panel = new VerticalPanel();
    final TabBar tabBar = new TabBar();
    final Label label = new Label();
    
    tabBar.addTab("Onglet 1");
    tabBar.addTab("Onglet 2");
    tabBar.addTab("Onglet 3");
    tabBar.addTab("Onglet 4");
    tabBar.addSelectionHandler(new SelectionHandler<Integer>() {
      @Override
      public void onSelection(SelectionEvent<Integer> event) {
        int courant = tabBar.getSelectedTab();
        int selectionne = event.getSelectedItem();
        
        if (courant != 0) {
          tabBar.setTabEnabled(3, true);
        }
        
        if (selectionne == 0) {
          tabBar.setTabEnabled(3, false);         
        }
        
        label.setText("Selection de l'onglet : "+tabBar.getTabHTML(selectionne)); 
      }
    });
   
    panel.add(tabBar);
    panel.add(label);
    tabBar.selectTab(0);
    
    RootPanel.get("app").add(panel);

 

80.8. Les panneaux (panels)

Ils permettent d'organiser les composants affichés sur la page : selon leurs fonctionnalités, ils peuvent gérer le positionnement des composants ou leur visibilité.

Les panneaux permettent d'assurer la structure visuelle de l'application. GWT propose un ensemble complet de composants graphiques de types conteneurs pour organiser et assembler les composants graphiques.

Les composants de type Panel contiennent d'autres composants et permettent de les organiser. Ils sont donc plus que des conteneurs car ils sont aussi des gestionnaires de positionnement.

Ceci est essentiellement dû au fait que le rendu HTML de ces composants est généralement soit un élément table soit un élément div HTML.

Panneau

Rendu HTML

Version de GWT

Description

AbsolutePanel

DIV

1.0

Panneau qui permet le positionnement absolu (grâce à leur position) des composants

CaptionPanel

 

1.5

Panneau qui possède un titre

CellPanel

TABLE

1.0

Panneau abstrait pour une cellule d'un panneau qui en est composé

ComplexPanel

 

1.0

Classe abstraite de base pour les panneaux possédant plusieurs composants

DeckPanel

DIV

 

Panneau qui n'affiche qu'un seul composant à la fois parmi ceux qu'il contient

DisclosurePanel

TABLE

1.4

 

DockPanel

TABLE

1.0

Panneau qui permet de positionner les composants dans 5 zones (N, S, E, W et centre)

FlexTable

 

1.0

 

FlowPanel

DIV

1.0

Panneau qui est un simple DIV

FocusPanel

DIV

1.0

 

FormPanel

DIV

1.1

Panneau qui contient un formulaire HTML

Frame

IFRAME

1.0

Panneau sous la forme d'un IFRAME

Grid

 

1.0

 

HorizontalPanel

TABLE

1.0

Panneau avec alignement horizontal des composants

HorizontalSplitPanel

DIV

1.4

Panneau composé de deux cellules l'une à côté de l'autre, redimensionnable en hauteur

HTMLPanel

DIV

1.0

Panneau qui permet d'afficher un contenu HTML

HTMLTable

     

LazyPanel

DIV

1.6

Panneau qui permet de différer la création de son rendu au moment de son affichage (GWT 1.6)

Panel

 

1.0

Classe abstraite de base pour les autres panels

PopupPanel

     

RootPanel

 

1.0

 

ScrollPanel

DIV

1.0

 

SimplePanel

DIV

1.0

Classe abstraite de base pour les panneaux ne possédant qu'un seul composant

StackPanel

TABLE

1.0

 

TabPanel

TABLE

1.0

Panneau sous la forme d'onglets

VerticalPanel

TABLE

1.0

Panneau avec alignement vertical des composants

VerticalSplitPanel

DIV

1.4

Panneau composé de deux cellules l'une au-dessus de l'autre, redimensionnable en hauteur


Comme pour les composants, ils possèdent une représentation en Java et en JavaScript. Le constructeur de la classe se charge de créer le ou les éléments nécessaires dans l'arbre DOM.

Chaque panneau peut contenir des composants ou d'autres panneaux. Le panneau principal est encapsulé dans la classe RootPanel.

Les autres panneaux servent de conteneur pour les composants graphiques.

en construction
La suite de cette section sera développée dans une version future de ce document

 

80.8.1. La classe Panel

La plupart des panneaux qui contiennent un ou plusieurs composants héritent de façon directe ou indirecte de la classe Panel.

Cette classe implémente l'interface HasWidgets. Cette interface définit plusieurs méthodes :

Méthode

Rôle

add(Widget)

Ajouter le composant fourni en paramètres

Clear()

Supprimer tous les composants contenus dans le panneau

iterator()

Obtenir un objet de type Iterator pour parcourir tous les composants inclus dans le panneau.

remove(Widget)

Supprimer le composant fourni en paramètre


Cette classe possède plusieurs classes filles directes : ComplexPanel, HorizontalSplitPanel, HTMLTable, SimplePanel, et VerticalSplitPanel.

 

80.8.2. La classe RootPanel

Cette classe encapsule la page affichée et l'associe au navigateur. C'est le seul panneau permettant cette association.

La classe RootPanel est la classe qui encapsule le panneau racine qui doit obligatoirement qui doit obligatoirement être au sommet de la hiérarchie des panneaux et composants.

Ce panneau encapsule une partie de la page html de l'application en permettant un accès à certains de ses éléments. Il n'est donc pas un conteneur au sens strict mais permet un accès aux éléments de l'arbre DOM de la page.

La méthode get() permet d'obtenir une référence sur l'élément du DOM dont l'id est fourni en paramètre de la méthode. Sans paramètre, cette méthode renvoie l'objet de type RootPanel qui encapsule la page.

Exemple :
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01
Transitional//EN">
<html>
  <head>
    <meta
http-equiv="content-type" content="text/html;
charset=ISO-8859-15">
    <link
type="text/css" rel="stylesheet" href="TestGWT.css">
   
<title>Application de test GWT</title>
    <script
type="text/javascript" language="javascript"
src="testgwt/testgwt.nocache.js"></script>
  </head>
  <body>
   
<h1>Application de test GWT</h1>
    <table
align="center">
      <tr>
        <td
id="menu"></td><td id="app"></td>
      </tr>
    </table>
  </body>
</html>

Il n'est pas possible d'instancier un objet de type RootPanel : il faut utiliser la méthode get() qui renvoie le panneau par défaut ou sa surcharge qui attend en paramètre l'id d'un élément de la page html de l'application.

Exemple :
    Label libelle = new Label("Bonjour");
   
    RootPanel.get("app").add(libelle);

Il est ainsi possible d'accéder à tous les éléments de la page html qui possèdent un id.

 

80.8.3. La classe SimplePanel

La classe SimplePanel est un panneau qui peut contenir un seul composant.

Ce panneau est implémenté sous la forme d'un simple tag DIV en HTML.

Exemple :
    SimplePanel panel = new SimplePanel();
    panel.setSize("200px", "50px");
    panel.addStyleName("monPanneau");
    Label label = new Label("Contenu du panneau");
    panel.add(label);
    RootPanel.get("app").add(panel);

Résultat :
.monPanneau {
border-color: black;
border-width : 1px;
border-style: solid;
background-color: silver;
text-align: center;
}

 

80.8.4. La classe ComplexPanel

La classe abstraite ComplexPanel est la classe de base pour un panneau qui peut contenir plusieurs composants.

Elle propose plusieurs méthodes de base pour gérer les composants contenus dans le panneau notamment :

Méthode

Rôle

add(Widget, Element);

Ajouter un nouveau composant

getChildren()

Obtenir une collection des composants contenus dans le panneau

getWidget(int)

Obtenir le composant à partir de son index

getWidgetCount()

Obtenir le nombre de composants contenus dans le panneau

getWidgetIndex(Widget)

Obtenir l'index d'un composant

insert(Widget, Element, int)

Insérer un nouveau composant à l'index fourni

iterator()

Obtenir un iterator sur les composants du panneau

remove(int)

Supprimer le composant à l'index fourni

remove(Widget)

Supprimer le composant


La classe ComplexPanel possède plusieurs classes filles notamment : AbsolutePanel, CellPanel, DeckPanel, FlowPanel, HTMLPanel et StackPanel.

 

80.8.5. La classe FlowPanel

La classe FlowPanel est un panneau dans lequel les composants sont ajoutés les uns à la suite des autres avec un passage à la ligne dès que la place manque pour contenir le composant. Ce panneau arrange les composants qu'il contient les uns à côté des autres en allant du haut à gauche vers le bas à droite.

Le panneau FlowPanel est un simple élément HTML <DIV> avec le style display:inline dans l'arbre DOM .

Exemple :
    FlowPanel panel = new FlowPanel();
    panel.setWidth("250px");
    panel.addStyleName("monPanneau");
    
    Button bouton = new Button("1");
    bouton.setWidth("80px"); 
    panel.add(bouton);
    
    bouton = new Button("2");
    bouton.setWidth("120px");
    panel.add(bouton);
    
    bouton = new Button("3");
    bouton.setWidth("50px");
    panel.add(bouton);
    
    bouton = new Button("4");
    bouton.setWidth("90px");
    panel.add(bouton);
    
    bouton = new Button("5");
    bouton.setWidth("80px");
    panel.add(bouton);
   
    RootPanel.get("app").add(panel);

 

80.8.6. La classe DeckPanel

La classe DeckPanel est un panneau qui affiche un composant à la fois. Ce panneau est utilisé dans un TabPanel pour afficher le contenu de l'onglet sélectionné.

Un exemple typique d'utilisation est pour la mise en oeuvre d'un assistant.

Exemple :
    final DeckPanel panel = new DeckPanel();
    panel.setSize("200px", "50px");
    panel.addStyleName("monPanneau");
    panel.add(new Label("Contenu cellule 1"));
    panel.add(new Label("Contenu cellule 2"));
    panel.add(new Label("Contenu cellule 3"));
    
    panel.showWidget(0);
    
    Timer timer = new Timer()
    {
      public void run()
      {
        int index = panel.getVisibleWidget();
        index++;
        if (index == panel.getWidgetCount()) { 
          index = 0;
        }
                   
        panel.showWidget(index);
      }
    };
   
    timer.scheduleRepeating(2000);
    
    RootPanel.get("app").add(panel);

 

80.8.7. La classe TabPanel

La classe TabPanel est un panneau composé d'onglets. Il est composé d'un ensemble de boutons, un pour chaque onglet et d'un composant DeckPanel qui affiche le contenu de l'onglet sélectionné.

Exemple :
    StringBuffer sb = new StringBuffer("<p>");
    TabPanel panel = new TabPanel();
    Panel contenu;
    contenu = new SimplePanel();
    contenu.add(new Label("Contenu du panneau 1"));
    panel.add(contenu, "Onglet 1");
    contenu = new SimplePanel();
    contenu.add(new Label("Contenu du panneau 2"));
    panel.add(contenu, "Onglet 2");
    contenu = new SimplePanel();
    contenu.add(new Label("Contenu du panneau 3"));
    panel.add(contenu, "Onglet 3");
    panel.selectTab(0);
    panel.setSize("500px", "250px");
    RootPanel.get("app").add(panel);

 

80.8.8. La classe FocusPanel

 

en construction
La suite de cette section sera développée dans une version future de ce document

 

80.8.9. La classe HTMLPanel

La classe HTMLPanel est un composant qui peut afficher du code HTML.

La classe HTMLPanel permet d'ajouter et de supprimer des éléments à partir de leur id.

Exemple :
    String html = "<table><tr><td nowrap><div id='libelle'>"
        + "</div></td><td><div id='saisie'>"
        + "</div></td></tr></table>";
    HTMLPanel panel = new HTMLPanel(html);
   
    panel.setSize("250px", "120px");
    panel.add(new Label("Libelle a saisir :"), "libelle");
    panel.add(new TextBox(), "saisie");
   
    RootPanel.get("app").add(panel);

 

80.8.10. La classe FormPanel

 

en construction
La suite de cette section sera développée dans une version future de ce document

 

80.8.11. La classe CellPanel

Cette classe est la classe abstraite pour une cellule d'un panneau qui en est composé. C'est la super classe de plusieurs panneaux : DockPannel, HorizontalPannel et VerticalPannel. Tous ces panneaux organisent les composants qu'ils contiennent dans des cellules.

 

80.8.12. La classe DockPanel

La classe DockPanel encapsule un panneau découpé en cinq parties positionnées relativement à la partie centrale :

Ce panneau utilise une table HTML.

Exemple :
    final DockPanel panel = new DockPanel();
    panel.setVerticalAlignment(HasAlignment.ALIGN_MIDDLE);
    panel.setHorizontalAlignment(HasAlignment.ALIGN_CENTER);
    panel.setWidth("250px");
    panel.setHeight("150px");
    panel.setBorderWidth(1);
    
    panel.add(new Label("North"), DockPanel.NORTH);
    panel.add(new Label("South"), DockPanel.SOUTH);
    panel.add(new Label("West"), DockPanel.WEST);
    panel.add(new Label("East"), DockPanel.EAST);
    panel.add(new Label("Center"), DockPanel.CENTER);

 

80.8.13. La classe HorizontalPanel

La classe HorizontalPanel encapsule un panneau qui peut contenir plusieurs composants alignés les uns à côté des autres.

Concrètement, c'est un tableau HTML où chaque composant est inséré dans une nouvelle cellule placée horizontalement à côté de la précédente.

Attention, même si sa taille est définie, le panneau n'est visible que s'il contient au moins un composant.

Exemple :
    HorizontalPanel panel = new HorizontalPanel();
    panel.addStyleName("monPanneau");
    
    Button menu1 = new Button("menu 1");
    Button menu2 = new Button("menu 2");
    Button menu3 = new Button("menu 3");
     
    panel.add(menu1);
    panel.add(menu2);
    panel.add(menu3);
    
    RootPanel.get("app").add(panel);

 

80.8.14. La classe VerticalPanel

La classe VerticalPanel encapsule un panneau qui peut contenir plusieurs composants alignés les uns au-dessus des autres. Ce panneau arrange les composants de façon verticale, les uns en dessous des autres comme dans une colonne.

Le panneau VerticalPanel est un élément HTML <TABLE> dans l'arbre DOM. A chaque appel de la méthode add(), une cellule est ajoutée dans une nouvelle ligne du tableau. Cette cellule contient le composant en paramètre de la méthode.

Exemple :
    VerticalPanel panel = new VerticalPanel();
    panel.addStyleName("monPanneau");
    
    Button menu1 = new Button("menu 1");
    Button menu2 = new Button("menu 2");
    Button menu3 = new Button("menu 3");
    
    panel.add(menu1);
    panel.add(menu2);
    panel.add(menu3);
    
    RootPanel.get("app").add(panel);

Attention, même si sa taille est définie, le panneau n'est visible que s'il contient au moins un composant..

 

80.8.15. La classe CaptionPanel

Ce panneau possède un titre : il permet de grouper des composants appartenant à un même ensemble fonctionnel.

Exemple :
    CaptionPanel panel = new CaptionPanel("Sexe");
    VerticalPanel vpBoutons = new VerticalPanel();
    vpBoutons.setStyleName("sexes");
    
    RadioButton rbHomme = new RadioButton("sexe","Homme");
    vpBoutons.add(rbHomme);
    RadioButton rbFemme = new RadioButton("sexe","Femme");
    vpBoutons.add(rbFemme);
    RadioButton rbInconnu = new RadioButton("sexe","Inconnu");
    vpBoutons.add(rbInconnu);
    panel.setContentWidget(vpBoutons);

 

80.8.16. La classe PopupPanel

La classe PopupPanel encapsule un panneau qui est capable de s'afficher au-dessus de tous les autres composants.

Il est possible que le panneau s'efface automatiquement (auto-hide) dès que l'utilisateur clique en dehors de celui-ci. Le panneau peut aussi être modal.

Ce panneau peut avoir de nombreuses utilités : afficher des données, demander une confirmation ou une petite quantité de données, verrouiller l'application, ...

Exemple :
  private static class TestPopupPanel extends PopupPanel {
    
    public TestPopupPanel(String message) {
      super(true, true);
     
      this.setStyleName("demo-popup");
     
      VerticalPanel contenuPopupPanel = new VerticalPanel();
     
      this.setAnimationEnabled(true);
      HTML titre = new HTML("Titre du PopupPanel");
     
      titre.setStyleName("demo-popup-header");
      HTML contenu = new HTML(message);
     
      contenu.setStyleName("demo-popup-message");
      
      // bouton pour fermer le popup
     
      ClickListener listener = new ClickListener() {
          public void onClick(Widget sender)
          {
            hide();
          }
      };
      Button boutonFermer = new Button("Fermer", listener);
      SimplePanel holder = new SimplePanel();
      holder.add(boutonFermer);
     
      holder.setStyleName("demo-popup-footer");
      contenuPopupPanel.add(titre);
      contenuPopupPanel.add(contenu);
      contenuPopupPanel.add(holder);
      
      this.setWidget(contenuPopupPanel);
    }

    public void onModuleLoad() {
      final TestPopupPanel popup = new TestPopupPanel("Contenu du popup");
    
      ClickListener listener = new ClickListener()
      {
        public void onClick(Widget sender)
        {
          popup.center();
        }
    };
    Button bouton = new Button("Afficher", listener);
   
    RootPanel.get("app").add(bouton);  
  }

Résultat :
.demo-popup
{
background-color       :    #ffc;
border                 :    1px solid #000;
}

.demo-popup-header
{
background-color       :    #ff0;
font-weight            :    bold;
border-bottom          :    1px solid #000;
padding                :    10px;
}

.demo-popup-message
{
padding                :    15px;
text-align             :    center;
}

.demo-popup-footer
{
padding                :    5px;
text-align             :    right;
width                  :    100%;
}

Cet exemple est purement éducatif car une partie de ses fonctionnalités est implémentée dans le composant DialogBox.

L'instance du panneau n'a pas besoin d'être rattachée au RootPanel ou à tout autre composant.

Les méthodes center() ou show() permettent d'afficher le panneau et la méthode hide() permet de le masquer.

La taille du panneau est déterminée en fonction de la taille du composant qu'il contient.

 

80.8.17. La classe DialogBox

 

en construction
La suite de cette section sera développée dans une version future de ce document

 

80.8.18. La classe DisclosurePanel

La classe DisclosurePanel est un panneau composé de deux parties : un en-tête toujours visible et une partie principale qu'il est possible de masquer ou d'afficher en cliquant sur l'en-tête.

Ce composant est pratique lorsqu'il y a beaucoup de données à afficher.

Exemple :
    final DisclosurePanel panel = new DisclosurePanel("Cliquez pour ouvrir");
    
    panel.addEventHandler(new DisclosureHandler() {
      public void onClose(DisclosureEvent event) {
        panel.getHeaderTextAccessor().setText("Cliquez pour ouvrir");
      }

      public void onOpen(DisclosureEvent event) {
        panel.getHeaderTextAccessor().setText("Cliquez pour fermer");
      }
    });
    
    panel.add(new Image("images/logo_java.jpg"));
   
    panel.setWidth("300px");
    RootPanel.get("app").add(panel);

Il est possible de personnaliser l'en-tête en dérivant d'un panneau par exemple HorizontalPanel et en fournissant une instance de cette classe à la méthode setHeader().

 

80.8.19. La classe AbsolutePanel

Ce panneau permet le positionnement absolu (grâce à des coordonnées) des composants dans le panneau. Le rendu de ce panneau en HTML est un div.

La taille du panneau n'est pas automatiquement agrandie lors de l'ajout de composant hors de sa surface d'affichage définie par sa taille.

Exemple :
    AbsolutePanel panel = new AbsolutePanel();
    panel.setSize("250px", "100px");
    panel.setStyleName("monPanneau");
    Label label1 = new Label("Mon premier texte");
    
    panel.add(label1, 50, 30);
    Label label2 = new Label("Mon second texte");
    panel.add(label2, 65, 45);
   
    RootPanel.get("app").add(panel);

 

80.8.20. La classe StackPanel

La classe StackPanel encapsule un composant qui contient plusieurs sous-panneaux possédant un titre. Seul le contenu d'un des sous-panneaux est affiché.

Exemple :
    StackPanel panel = new StackPanel();
    Label label;
    label = new Label("Contenu 1");
    panel.add(label, "Titre 1", false);
    label = new Label("Contenu 2");
    panel.add(label, "Titre 2", false);
    label = new Label("Contenu 3");
    panel.add(label, "Titre 2", false);
    panel.setSize("200px", "200px");
    RootPanel.get("app").add(panel);

 

80.8.21. La classe ScrollPanel

La classe ScrollPanel est un panneau qui peut contenir un seul composant et qui possède une barre de défilement.

Exemple :
    StringBuffer sb = new StringBuffer("<p>");
    for (int i=0; i<10; i++) {
      sb.append("ligne de test ... <br>");
    }
   
    sb.append("</p>");
    ScrollPanel panel = new ScrollPanel(new HTML(sb.toString()));
   
    panel.setSize("200px", "100px");
   
    RootPanel.get("app").add(panel);

 

80.8.22. La classe FlexTable

La classe FlexTable encapsule un panneau qui est une table dont le nombre de cellules peut varier pour chaque ligne. A sa création, une FlexTable n'a pas de taille explicite.

Ce panneau utilise une table HTML.

L'index de la première cellule vaut 0.

Exemple :
    Personne[] personnes = new Personne[] {
        new Personne(1, "Nom 1", "Prenom 1", 171),
        new Personne(2, "Nom 2", "Prenom 2", 172),
        new Personne(3, "Nom 3", "Prenom 3", 173) };
    FlexTable t = new FlexTable();
   
    t.setTitle("Personnes");
    t.setText(0, 0, "Id");
    t.setText(0, 1, "Nom");
    t.setText(0, 2, "Prenom");
    t.setText(0, 3, "Taille");
   
    t.setCellPadding(5);
    t.setCellSpacing(0);
   
    t.setBorderWidth(1); 
    for (int i = 0; i < 4; i++) {
      t.getColumnFormatter().addStyleName(i, "monPanneau");
    }
    for (int i = 0; i < personnes.length; i++) {
      Personne personne = personnes[i];
      t.setText(i + 1, 0, "" + personne.getId());
      t.setText(i + 1, 1, personne.getNom());
      t.setText(i + 1, 2, personne.getPrenom());
      t.setText(i + 1, 3, "" + personne.getTaille());
    }
   
    RootPanel.get("app").add(t);

La méthode setColSpan() de la classe FlexCellFormater permet de fusionner deux cellules.

 

80.8.23. La classe Frame

Ce composant encapsule un IFrame HTML.

Exemple :
    Frame frame = new Frame("http://www.google.fr/");
    frame.setWidth("600px");
    frame.setHeight("350px");
   
    RootPanel.get("app").add(frame);

Les Iframes sont fréquemment utilisés pour effectuer des opérations depuis un site distant à l'insu de l'utilisateur. Lors de l'affichage de l'application utilisant un IFrame un message d'avertissement est affiché en demandant la confirmation de l'accès au site.

 

80.8.24. La classe Grid

Ce composant encapsule un tableau HTML : c'est donc une grille composée de cellules.

Il faut définir le nombre de cellules de la grille (nombre de colonnes et de lignes) avant de pouvoir insérer un composant dans une cellule.

L'ajout d'un composant dans une cellule se fait en utilisant la méthode setWidget().

Exemple :
    Grid grille = new Grid(3, 3); 
    grille.setSize("250px", "100px");
    
    for (int i = 0; i < 3 ; i++ ) {
      for (int j = 0; j < 3 ; j++ ) {
        grille.setWidget(i, j, new Label("Libelle "+(i+j)));
      }
    }
    
    RootPanel.get("app").add(grille);

La méthode setText() permet de facilement remplir une cellule de la grille avec du texte. L'exemple ci-dessous est identique au précédent.

Exemple :
    Grid grille = new Grid(3, 3); 
    grille.setSize("250px", "100px");
    
    for (int i = 0; i < 3 ; i++ ) {
      for (int j = 0; j < 3 ; j++ ) {
        grille.setText(i, j, "Libelle "+(i+j));
      }
    }
    
    RootPanel.get("app").add(grille);

Si la cellule à remplir est en dehors de la taille définie dans le constructeur alors une exception de type IndexOutOfBoundsException est levée.

Il est possible de redimensionner le nombre de cellules de la grille grâce aux méthodes resize(), resizeColumns() et resizeRows().

 

80.8.25. La classe HorizontalSplitPanel

La classe HorizontalSplitPanel encapsule un panneau composé de deux cellules l'une à côté de l'autre. La taille des cellules est adaptable, l'une au détriment de l'autre. Si la taille d'une cellule est trop petite pour afficher l'ensemble de son contenu alors une barre de défilement est ajoutée.

Exemple :
    HorizontalSplitPanel panel = new HorizontalSplitPanel();
    panel.setSize("500px", "200px");
    panel.setLeftWidget(new HTML("<H1>Contenu de la partie gauche</H1>"));
    panel.setRightWidget(new HTML("<H1>Contenu de la partie droite</H1>"));
   
    RootPanel.get("app").add(panel);

 

80.8.26. La classe VerticalSplitPanel

La classe VerticalSplitPanel encapsule un panneau composé de deux cellules l'une au-dessus de l'autre. La taille des cellules est adaptable, l'une au détriment de l'autre.Si la taille d'une cellule est trop petite pour afficher l'ensemble de son contenu alors une barre de défilement est ajoutée.

Exemple :
   VerticalSplitPanel panel = new VerticalSplitPanel();
    panel.setSize("500px", "300px");
    panel.setTopWidget(new HTML("<H1>Contenu de la partie haute</H1>"));
    panel.setBottomWidget(new HTML("<H1>Contenu de la partie basse</H1>"));
    RootPanel.get("app").add(panel);

 

80.8.27. La classe HTMLTable

Cette classe abstraite est la classe mère des classes Grid et Flextable.

Les classes HTMLTable.CellFormatter, HTMLTable.ColumnFormatter et HTMLTable.RowFormatter permettent de formater respectivement le contenu d'une cellule, d'une colonne ou d'une ligne d'une table.

 

80.8.28. La classe LazyPanel

La classe LazyPanel est un composant qui est instancié au moment de son affichage.

Ceci peut permettre d'améliorer le temps de démarrage d'une application car les parties non affichées implémentées avec un LazyPanel ne sont plus instanciées au lancement de l'application mais uniquement au moment de leur affichage.

Pour utiliser un tel panneau, il faut hériter de la classe LazyPanel et redéfinir la méthode abstraite createWidget() en incluant le code de la création du rendu du panneau.

La méthode createWidget() sera invoquéee lorsque la méthode setVisible() du panneau sera utilisée.

Exemple :
  private static class TestLazyPanel extends LazyPanel {
    @Override
    protected Widget createWidget() {
      return new Label("Bonjour");
    }
  }

  public void onModuleLoad() {
    final Panel panel = new TestLazyPanel();
    
    panel.setVisible(false);
    PushButton bouton = new PushButton("Afficher");    
    bouton.addClickHandler(new ClickHandler() {
      public void onClick(ClickEvent event) {
        panel.setVisible(true);
      }
    });
    
    RootPanel root = RootPanel.get("app");
    root.add(bouton);
    root.add(panel);
  }

 

80.9. La création d'éléments réutilisables

GWT permet de créer ses propres composants graphiques et permet aussi de créer des modules qui peuvent être utilisés par plusieurs projets.

 

80.9.1. La création de composants personnalisés

Le plus simple est de créer une classe qui hérite de la classe Composite mais il est aussi possible d'hériter d'un composant existant pour l'enrichir.

Le composant Composite permet de créer un nouveau composant autonome par composition. Il faut obligatoirement faire un appel à la méthode initWidget() à la fin du constructeur en lui passant en paramètre le panneau qui contient les éléments graphiques ou un composant.

en construction
La suite de cette section sera développée dans une version future de ce document

 

80.9.2. La création de modules réutilisables

Il est possible de développer un module qui va contenir des composants graphiques personnalisés, des classes dédiées ou des ressources, comme des images, afin de permettre sa réutilisation dans plusieurs applications GWT.

Ce module doit être packagé sous la forme d'un fichier .jar

en construction
La suite de cette section sera développée dans une version future de ce document

 

80.10. Les événements

Une application GWT est pilotée par des événements émis selon les actions de l'utilisateur sur les composants de l'application. La plupart des composants graphiques proposent l'émission d'événements en réaction aux actions de l'utilisateur.

La gestion des événements met en oeuvre des listeners d'une façon similaire à AWT ou Swing. Un listener est une interface qui doit être implémentée pour que les méthodes, appelées selon l'événement, effectuent les traitements à réaliser.

Des classes de types Adapter sont proposées afin de faciliter l'écriture de certains listeners : elles implémentent une interface de type Listener en définissant toutes les méthodes sans traitement. Il suffit de redéfinir la ou les méthodes requises en fonction des besoins.

Nom

Adapter

Méthodes

ChangeListener

 

void onChange(Widget sender)

ClickListener

 

void onClick(Widget sender)

EventListener

 

 

FocusListener

FocusListenerAdapter

void onFocus(Widget sender)
void onLostFocus(Widget sender)

KeyboardListener

KeyboardListenerAdapter

void onKeyDown(Widget sender, char keyCode, int modifiers)
void onKeyPress(Widget sender, char keyCode, int modifiers)
void onKeyUp(Widget sender, char keyCode, int modifiers)

LoadListener

 

void onError(Widget sender)
void onLoad(Widget sender)

MouseListener

MouseListenerAdapter

void onMouseDown(Widget sender, int x, int y)
void onMouseEnter(Widget sender)
void onMouseLeave(Widget sender)
void onMouseMove(Widget sender, int x, int y)
void onMouseUp(Widget sender, int x, inty)

PopupListener

 

void onPopupClosed(PopupPanel sender, boolean autoClosed)

ScrollListener

 

void onScroll(Widget sender, int scrollLeft, int scrollTop)

TableListener

 

void onCellClicked(SourcedTableEvents sender, int row, int cell)

TabListener

 

void onBeforeTabSelected(SourcesTabEvents sender, int tabIndex)
void onTabSelected(SourcesTabEvents sender, int tabIndex)

TreeListener

 

void onTreeItemSelected(TreeItem item)
void onTreeItemStateChanged(TreeItem item)


Exemple :
  Button b = new Button("Valider");
  b.addClickListener(new ClickListener() {
    public void onClick(Widget sender) {
      // traitements lors du clic sur le bouton
    }
  });

Exemple :
  TextBox t = new TextBox();
  t.addKeyboardListener(new KeyboardListenerAdapter() {
    public void onKeyPress(Widget sender, char keyCode, int modifiers) {
    // traitements lors de l'appui sur une touche
    }
  });

 

80.11. JSNI

JSNI (JavaScript Native Interface) permet d'inclure du code JavaScript dans le code Java. Cette API a plusieurs utilités :

JSNI est utilisé par le compilateur pour fusionner le code JavaScript qu'il contient avec le code JavaScript généré à la compilation.

Le code JavaScript est inclus dans une méthode qualifiée avec le modificateur natif. Le code JavaScript lui-même est inclus entre les caractères /*-{ et }-*/;

Cette séquence de caractères à l'avantage d'être ignorée par le compilateur Java et exploitée par le compilateur de GWT.

Exemple :
public static native void alert(String msg) /*-{ 
    $wnd.alert(msg); 
}-*/;

Il est possible de passer des paramètres qui seront utilisés par le code JavaScript.

Exemple :
public native int ajouter (int val1, int val2)
/*-{
var result = val1 + val2;
return result;
}-*/;

Il est possible de fournir en paramètre d'une méthode native des objets Java. Une syntaxe particulière permet d'utiliser ces objets dans le code JavaScript de la méthode : soit pour accéder à un champ ou pour invoquer une méthode.

Pour accéder à un champ d'un objet Java dans du code Javscript, il faut utiliser la syntaxe :

objet.@classe::champ

objet est la référence sur l'objet passé en paramètre
classe est le nom pleinement qualifié de la classe de l'objet
champ est le nom du champ à accéder

Pour utiliser une méthode d'un objet Java dans du code JavaScript, il faut utiliser la syntaxe :
objet.@classe::methode(signature)(parametres)

Il est nécessaire de préciser la signature de la méthode car celle-ci peut être surchargée et cela permet ainsi de préciser la version qui doit être utilisée. La signature est précisée en suivant une convention spéciale pour chaque type utilisable.

Type dans la signature

Type Java

B

byte

S

short

I

int

J

long

F

float

D

double

C

char

Z

boolean

Lnom/pleinement/qualifie/classe;

nom.pleinement.qualifie.classe

[type

type[]


Certaines variables spécifiques sont définies dans JSNI.

variable

Rôle

$wnd

objet JavaScript Window

$doc

objet JavaScript Document


Exemple :
public class Alert {
  public static native void alert(String msg) /*-{
    $wnd.alert(msg);
  }-*/;
}

button1.addClickListener(new ClickListener() {
public void onClick(Widget sender) {
Alert.alert("clicked!" );
}
});

L'inconvénient de JNSI est que le code JavaScript n'est vérifiable qu'à l'exécution.

 

80.12. La configuration et l'internationalisation

GWT propose deux mécanismes pour internationaliser une application :

Gwt propose deux mécanismes pour faciliter la mise en oeuvre de fonctionnalités de configuration de l'application :

La configuration statique est mise en oeuvre grâce aux interfaces Constants ou Messages

Il faut étendre l'une ou l'autre de ces interfaces et définir des méthodes de type getter pour chaque propriété.

La configuration dynamique est mise en oeuvre grâce à la classe Dictionary.

 

80.12.1. La configuration

en construction
La suite de cette section sera développée dans une version future de ce document

 

80.12.2. L'internationalisation

L'internationalisation (I18N) permet de fournir le support de plusieurs langues pour une application. Même si son support n'est pas prévu, il peut être intéressant d'utiliser le mécanisme d'internationalisation pour centraliser les textes affichés par l'application. Ceci permet notamment de faciliter les vérifications orthographiques et grammaticales et la modification des textes sans altérer le code source.

Il faut définir un fichier de propriétés stocké dans le package client. Ce fichier contient sur chaque ligne une paire clé/valeur séparée par un caractère « = ».

Exemple : le fichier MonAppMessages.properties
menu1=Fichier
menu2=Editer

Il est possible de définir des paramètres dans les valeurs. Chacun de ces paramètres est numéroté à partir de 0. Un paramètre est défini en utilisant son numéro entouré par des accolades.

Exemple :
erreur=La valeur saisie doit être comprise entre {0} et {1}

Il faut définir un fichier de propriétés pour chaque langue proposée par l'application. Le nom de fichier doit être identique au fichier de propriétés initial suivi par un caractère underscore et le code langue. Les clés doivent être identiques et les valeurs doivent contenir leurs traductions.

Exemple : le fichier MonAppMessages_en.properties
menu1=File
menu2=Edit

Ensuite, créer une interface qui porte le nom du fichier de propriétés et qui hérite de l'interface com.google.gwt.i18n.client.Messages. Il faut définir une méthode qui renvoie une chaîne de caractères pour chaque clé définie dans le fichier de propriétés. Le nom de cette méthode doit correspondre exactement au nom de chaque clé.

Exemple :
package com.jmdoudoux.testgwt.client;

import com.google.gwt.i18n.client.Messages;

public interface MonAppMessages extends Messages {
  String menu1();
  String menu2();
}

Si des paramètres sont définis dans la valeur, il faut ajouter autant de paramètres à la méthode correspondante.

Il faut modifier le fichier de configuration de l'application en ajoutant un tag <inherits name="com.google.gwt.i18n.I18N"/> pour indiquer à GWT d'ajouter le support de l'internationalisation.

Il faut aussi ajouter un tag <extend-property> possédant un attribut name dont la valeur doit être égale à la Locale et un attribut values dont la valeur doit contenir le ou les codes langues supportés par l'application.

Exemple :
<module>
  <!-- Inherit the core Web Toolkit stuff. -->
  <inherits name='com.google.gwt.user.User' />
  <inherits name="com.google.gwt.i18n.I18N"/>
  <stylesheet src="MonApp.css" />
  <!-- Specify the app entry point class. -->
  <entry-point class='com.jmdoudoux.testgwt.client.MonApp' />
            
  <extend-property name="locale" values="en"/>
</module>

Dans le code de l'application, il faut utiliser la méthode GWT.Create() en fournissant en paramètre la classe correspondant à l'interface définie.

Exemple :
MonAppMessages messages = (MonAppMessages) GWT.create(MonAppMessages.class);

Il suffit d'utiliser l'objet instancié pour obtenir la valeur dont la clé correspond au nom de la méthode invoquée.

Exemple :
    // menu.addItem("menu1", menu1);
    menu.addItem(messages.menu1(), menu1);
    // menu.addItem("menu2", menu2);
    menu.addItem(messages.menu2(), menu2);<

Au lancement de l'application, la langue par défaut est utilisée.

Pour afficher l'application dans une autre langue, il faut ajouter dans l'url le paramètre locale avec, comme valeur, le code langue désiré.

 

80.13. L'appel de procédures distantes (Remote Procedure Call)

GWT propose plusieurs solutions pour permettre l'appel de traitements côté serveur :

GWT propose des fonctionnalités pour permettre l'appel de procédures sur le serveur et ainsi mettre en oeuvre des fonctionnalités de type AJAX.

Le code côté serveur peut être réalisé avec n'importe quel langage proposant un support du traitement des requêtes HTTP. Ceci inclus Java EE notamment en utilisant des servlets. Une solution reposant sur Java côté serveur est cependant la plus facile à mettre en oeuvre.

En utilisant Java, GWT fournit deux classes qui encapsulent l'utilisation de l'objet JavaScript XMLHttpRequest :

Comme dans toute application de type Ajax, il est important d'indiquer à l'utilisateur que des traitements sont en cours : cela peut se faire par exemple à l'aide d'une zone de texte spéciale, d'une image animée, d'un changement de la forme du curseur, ...

 

80.13.1. GWT-RPC

GWT permet aux applications de communiquer avec le serveur au travers de son propre mécanisme d'appels de type RPC. Ce mécanisme assure la sérialisation des objets qui sont échangés entre la partie cliente en JavaScript et la partie serveur écrite en Java. Cette sérialisation n'est pas réalisée au travers d'un standard tel que XML, JSON, SOAP ou XML-RPC, mais elle met en oeuvre son propre format.

Les appels réalisés par l'application sont de type asynchrone.

Cette solution utilise, côté serveur, des servlets qui héritent de la classe RemoteServiceServlet.

L'implémentation d'un service nécessite plusieurs étapes :

 

80.13.1.1. Une mise oeuvre avec un exemple simple

Cette section va développer une petite application qui demande à l'utilisateur de saisir son prénom, invoque un service sur le serveur et affiche le message de salutation retourné par le service.

Exemple de projet dans Netbeans :

Dans l'exemple, les classes et interfaces sont regroupées dans deux sous-packages services au niveau de la partie client et de la partie serveur. Ceci n'est pas une obligation mais permet un meilleur découpage des sources.

GWT propose un mécanisme qui permet l'échange d'objets Java entre le client et le serveur. Pour mettre en oeuvre ce mécanisme il est nécessaire de définir trois entités :

Entités

localisation

Rôle

interface du service

Client et serveur

Décrit le service : la signature des méthodes

classe du service

Serveur

Implémentation du service

interface asynchrone du service

Client

Permet l'appel au service de façon asynchrone


L'interface du service est définie dans le sous-packages client/services. Elle hérite impérativement de l'interface com.google.gwt.user.client.rpc.RemoteService et va contenir les méthodes utilisables.

Exemple :
package com.jmdoudoux.test.gwt.client.services;

import com.google.gwt.user.client.rpc.RemoteService;

public interface MonServiceGWT extends RemoteService {
    public String saluer(String s);
}

L'interface pour l'appel asynchrone du service est définie dans le sous-package client/services de l'application. Par convention, elle possède le même nom que l'interface du service suffixé par Async.

Elle doit définir la méthode qui permettra l'invocation asynchrone de la méthode correspondante sur le serveur. Cette méthode doit avoir les caractéristiques suivantes :

Exemple :
package com.jmdoudoux.test.gwt.client.services;

import com.google.gwt.user.client.rpc.AsyncCallback;

public interface MonServiceGWTAsync {

    public void saluer(String s, AsyncCallback asyncCallback);
}

Pour la partie serveur, il faut définir une servlet dans le sous-package server/services qui hérite de com.google.gwt.server.rpc.RemoteServiceServlet et qui implémente l'interface du service.

Par convention, la classe de cette servlet possède le même nom que l'interface du service suffixé par Impl puisque c'est l'implémentation concrète du service

Exemple :
package com.jmdoudoux.test.gwt.server.services;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.jmdoudoux.test.gwt.client.services.MonServiceGWT;

public class MonServiceGWTImpl extends RemoteServiceServlet implements
        MonServiceGWT {
    
    public String saluer(String s) {
        return "Bonjour " + s;
    }
}

Remarque : pour des raisons de simplicité, dans l'exemple ci-dessus, la servlet implémente les traitements du service. Il serait préférable de découpler la servlet qui hérite de RemoteServiceServlet et implémente l'interface du service. Pour cela, définir un objet métier de type POJO qui implémente l'interface du service : chaque méthode de l'interface de la servlet se charge d'invoquer la méthode correspondante de l'objet métier.

La servlet RemoteServiceServlet héritée pour l'implémentation du service propose quelques méthodes utiles.

La méthode getThreadLocalRequest() permet d'obtenir un objet de type HttpServletRequest qui encapsule la requête http.

La méthode getThreadLocalResponse() permet d'obtenir un objet de type HttpServletResponse qui encapsule la réponse http.

En résumé, voici une synthèse des entités à créer pour un service

Entités

Hérite de

Rôle

MonServiceGWT

RemoteService

Interface qui décrit le service. Utilisé côté client et serveur

MonServiceGWTAsync

 

Interface pour l'appel asynchrone.

Son nom est composé par convention du nom de l'interface du service suffixé par Async.

Contient toutes les méthodes de l'interface du service, sans valeur de retour, sans exception et avec les même paramètres plus un dernier paramètre de type AsyncCallback

Utilisé côté client uniquement

MonServiceGWTImpl

RemoteServiceServlet

Implémentation concrète de l'interface du service

Utilisé côté serveur uniquement


L'utilisation de GWT-RPC passe par l'objet JavaScript XMLHttpRequest. Les données sont donc échangées entre le client et le serveur sous un mécanisme propre à GWT : les objets doivent donc être sérialisés côté client et désérialisés côté serveur. Côté serveur, c'est la classe RemoteServiceServlet qui automatise cette tâche.

Il faut déclarer la servlet dans le fichier web.xml de la webapp.

Exemple :
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
  http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <servlet>
      <servlet-name>MonServiceGWT</servlet-name>
      <servlet-class>com.jmdoudoux.test.gwt.server.services.MonServiceGWTImpl</servlet-class>
    </servlet>
    <servlet-mapping>
      <servlet-name>MonServiceGWT</servlet-name>
      <url-pattern>/com.jmdoudoux.test.gwt.Main/services/monservicegwt</url-pattern>
    </servlet-mapping>
    <session-config>
      <session-timeout>30</session-timeout>
    </session-config>
    <welcome-file-list>
      <welcome-file>welcomeGWT.html</welcome-file>
    </welcome-file-list>
</web-app>

Pour utiliser le serveur Tomcat embarqué avec GWT en mode hosted, il faut déclarer la servlet dans le fichier de configuration du module pour que le client puisque invoquer le service.

Pour déclarer la servlet du service dans le fichier de configuration du module, il faut utiliser un tag servlet ayant deux attributs :

Exemple :
<servlet path="/services/monservicegwt" 
       class="com.jmdoudoux.test.gwt.server.services/MonServiceGWTImpl"/>

GWT va automatiquement référencer la servlet, dans le conteneur Tomcat, avec le chemin fourni pour permettre son invocation par le client lors de son exécution dans le mode hosted.

GWT ne propose que des échanges asynchrones avec le serveur puisqu'ils utilisent l'objet JavaScript XMLHttpRequest.

L'invocation d'un service RPC dans la partie cliente de l'application nécessite plusieurs étapes :

  1. obtenir une instance de l'interface d'appel asynchrone du service en invoquant la méthode create() de la classe GWT
  2. caster l'instance vers le type ServiceDefTarget
  3. invoquer la méthode setServiceEntryPoint() en lui passant en paramètre l'url de la servlet qui implémente le service
  4. créer une instance de la classe AsyncCallBack() qui implémente les traitements à réaliser en cas de succès et d'échec de l'appel du service
  5. invoquer la méthode de l'interface appel asynchrone en lui passant en paramètre les paramètres d'appel du service et l'instance de type callback

Pour obtenir une instance de l'interface d'appel asynchrone du service, il faut invoquer la méthode create() de la classe GWT et caster le retour vers le type de l'interface asynchrone : cette instance sera le proxy qui permettra l'appel du service distant.

Pour préciser l'url d'appel du service, il faut caster l'instance de ce service en un objet de type ServiceDefTarget et invoquer sa méthode setServiceEntryPoint() en lui passant en paramètre l'url.

Le service doit être hébergé sur le même domaine et le même port du serveur que celui qui a fourni la page HTML au navigateur.

Le plus simple est de créer une méthode statique qui renvoie l'instance du type de l'interface asynchrone

Exemple :
    public static MonServiceGWTAsync getService() {
        MonServiceGWTAsync service = (MonServiceGWTAsync) GWT.create(MonServiceGWT.class);
        ServiceDefTarget endpoint = (ServiceDefTarget) service;
        String moduleRelativeURL = GWT.getModuleBaseURL() +
          "services/monservicegwt";
        endpoint.setServiceEntryPoint(moduleRelativeURL);
        return service;
    }

Il faut créer une instance d'un objet qui implémente l'interface com.google.gwt.client.rpc.asyncCallback. Le plus simple est de définir une classe anonyme interne. L'interface AsyncCallback définit deux méthodes

Dans la méthode onSuccess(), il faut caster l'objet passé en paramètre qui contient le résultat de l'appel vers l'objet du type adéquat.

Exemple :
        // Instanciation d'un callback asynchrone pour traiter la réponse
        final AsyncCallback callback = new AsyncCallback() {

            public void onSuccess(Object result) {
                lblMessage.setText((String) result);
            }

            public void onFailure(Throwable caught) {
                lblMessage.setText("Echec de la communication : " + caught.getMessage());
            }
        };

Pour invoquer le service, il faut obtenir une instance du proxy et invoquer la méthode voulue en lui passant les paramètres à fournir au service et l'instance de l'interface AsynCallback qui prend en charge le retour de l'appel.

Exemple :
                        getService().saluer(text.getText(), callback);

Il n'est pas possible de fournir null comme callbak même si aucun retour n'est attendu suite à l'appel au service.

L'exemple complet du code de l'application permet à l'utilisateur de saisir son prénom, d'invoquer le service et d'afficher le résultat de l'appel.

Exemple :
package com.jmdoudoux.test.gwt.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
import com.jmdoudoux.test.gwt.client.services.MonServiceGWT;
import com.jmdoudoux.test.gwt.client.services.MonServiceGWTAsync;

public class MainEntryPoint implements EntryPoint {

    private Label lblMessage = new Label();
    private TextBox text = new TextBox();
    private Button button = new Button();

    public MainEntryPoint() {
    }

    public void onModuleLoad() {
        text.setText("");
        button.setText("Saluer");

        // Instanciation d'un callback asynchrone pour traiter la réponse
        final AsyncCallback callback = new AsyncCallback() {

            public void onSuccess(Object result) {
                lblMessage.setText((String) result);
            }

            public void onFailure(Throwable caught) {
                lblMessage.setText("Echec de la communication : " + caught.getMessage());
            }
        };

        button.addClickListener(new ClickListener() {

            public void onClick(Widget sender) {
                // invocation du service
                getService().saluer(text.getText(), callback);
            }
        });
        
        Panel main = new FlowPanel();
        RootPanel.get().add(main);
        main.add(text);
        main.add(button);
        main.add(lblMessage);
    }

    public static MonServiceGWTAsync getService() {
        MonServiceGWTAsync service = (MonServiceGWTAsync) GWT.create(MonServiceGWT.class);
        ServiceDefTarget endpoint = (ServiceDefTarget) service;
        String moduleRelativeURL = GWT.getModuleBaseURL() + "services/monservicegwt";
        endpoint.setServiceEntryPoint(moduleRelativeURL);
        return service;
    }
}

Le diagramme de classe ci-dessous décrit l'ensemble des classes et interfaces utilisées.

Il n'y a pas de relation au sens POO entre l'interface du service et l'interface d'appel asynchrone du service.

Pour un meilleur découpage du code source, il est possible de définir une classe dédiée qui implémente l'interface AsyncCallback

exemple : le code de l'application

Exemple :
package com.jmdoudoux.test.gwt.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
import com.jmdoudoux.test.gwt.client.services.MonServiceGWT;
import com.jmdoudoux.test.gwt.client.services.MonServiceGWTAsync;

public class MainEntryPoint implements EntryPoint {

    private Label lblMessage = new Label();
    private TextBox text = new TextBox();
    private Button button = new Button();

    public MainEntryPoint() {
    }

    public void onModuleLoad() {
        text.setText("");
        button.setText("Saluer");

        button.addClickListener(new ClickListener() {

            public void onClick(Widget sender) {
                // invocation du service
                getService().saluer(text.getText(), new MonAsyncCallback(lblMessage));
            }
        });
        
        Panel main = new FlowPanel();
        RootPanel.get().add(main);
        main.add(text);
        main.add(button);
        main.add(lblMessage);
    }

    public static MonServiceGWTAsync getService() {
        MonServiceGWTAsync service = (MonServiceGWTAsync) GWT.create(MonServiceGWT.class);
        ServiceDefTarget endpoint = (ServiceDefTarget) service;
        String moduleRelativeURL = GWT.getModuleBaseURL() + "services/monservicegwt";
        endpoint.setServiceEntryPoint(moduleRelativeURL);
        return service;
    }
}

exemple : la classe MonAsyncCallback

Exemple :
package com.jmdoudoux.test.gwt.client;

import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Label;

public class MonAsyncCallback implements AsyncCallback {

    Label label;

    public MonAsyncCallback(Label label) {
        this.label = label;
    }

    public void onSuccess(Object result) {
        label.setStyleName("message");
        label.setText((String) result);
    }

    public void onFailure(Throwable caught) {
        label.setStyleName("erreur");
        label.setText("Echec de la communication");
    }
}

Il faut se souvenir dans les développements qu'une application JavaScript est monothread. Plusieurs callbacks ne peuvent donc pas être exécutés en simultané.

 

80.13.1.2. La transmission d'objets lors des appels aux services

Tout objet qui sera utilisé dans un échange de type GWT-RPC doit implémenter l'interface com.google.gwt.user.client.rpc.IsSerializable ou l'interface java.io.Serializable ( Depuis GWT 1.4). L'usage de l'interface Serializable est recommandé car cela permet à l'objet de rester indépendant de GWT.

Les attributs déclarés transient ne seront pas sérialisés lors des échanges.

Il est nécessaire que l'objet possède un constructeur sans argument.

Exemple :
package com.jmdoudoux.test.gwt.client.vo;

import java.io.Serializable;

public class Personne implements Serializable {

    private String nom;
    private String prenom;
    private int taille;
    
    public Personne() {
    }

    public Personne(String nom, String prenom, int taille) {
        this.nom = nom;
        this.prenom = prenom;
        this.taille = taille;
    }
    
    public String getNom() {
        return nom;
    }

    public void setNom(String nom) {
        this.nom = nom;
    }

    public String getPrenom() {
        return prenom;
    }

    public void setPrenom(String prenom) {
        this.prenom = prenom;
    }

    public int getTaille() {
        return taille;
    }

    public void setTaille(int taille) {
        this.taille = taille;
    }
}

Le service peut alors utiliser l'objet en paramètre d'entrée ou de sortie.

Exemple :
package com.jmdoudoux.test.gwt.client.services;

import com.google.gwt.user.client.rpc.RemoteService;
import com.jmdoudoux.test.gwt.client.vo.Personne;

public interface PersonneService extends RemoteService{
    public Personne obtenirParId(int id);
    
    public Personne[] obtenirToutes();
}

L'interface d'appel asynchrone du service ne fait pas référence au bean.

Exemple :
package com.jmdoudoux.test.gwt.client.services;

import com.google.gwt.user.client.rpc.AsyncCallback;

public interface PersonneServiceAsync {

    public abstract void obtenirParId(int id, AsyncCallback asyncCallback);

    public abstract void obtenirToutes(AsyncCallback asyncCallback);
}

L'implémentation du service peut utiliser toutes les API nécessaires à ses traitements, notamment celles relatives aux accès à une base de données pour extraire les informations requises. Dans l'exemple ci-dessous, les données sont simplement instanciées.

Exemple :
package com.jmdoudoux.test.gwt.server;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.jmdoudoux.test.gwt.client.services.PersonneService;
import com.jmdoudoux.test.gwt.client.vo.Personne;

public class PersonneServiceImpl extends RemoteServiceServlet implements
        PersonneService {
    
    public Personne obtenirParId(int id) {
        return new Personne("nom"+id,"prenom"+id,170+id);
    }

    public Personne[] obtenirToutes() {
        Personne[] resultat = new Personne[5];
        for (int i = 1 ; i < 6 ; i++) {
          resultat[i] = new Personne("nom"+i,"prenom"+i,170+i);
        }
        return resultat;
    }
}

Dans l'application, il suffit de caster le résultat de l'invocation du service vers le type du bean pour obtenir une instance du bean contenant les données transmises par le serveur.

Exemple :
package com.jmdoudoux.test.gwt.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
import com.jmdoudoux.test.gwt.client.services.PersonneService;
import com.jmdoudoux.test.gwt.client.services.PersonneServiceAsync;
import com.jmdoudoux.test.gwt.client.vo.Personne;

public class MainEntryPoint implements EntryPoint {

    private Label lblMessage = new Label();
    private Label lblNom = new Label("Nom : ");
    private Label lblPrenom = new Label("Prénom : ");
    private Label lblTaille = new Label("Taille : ");
    private TextBox textNom = new TextBox();
    private TextBox textPrenom = new TextBox();
    private TextBox textTaille = new TextBox();
    private Button button = new Button("Obtenir données");

    public MainEntryPoint() {
    }

    public void onModuleLoad() {

        // Instanciation d'un callback asynchrone pour traiter la réponse
        final AsyncCallback callback = new AsyncCallback() {

            public void onSuccess(Object result) {
                Personne personne = (Personne) result;
                textNom.setText(personne.getNom());
                textPrenom.setText(personne.getPrenom());
                textTaille.setText(""+personne.getTaille());
            }

            public void onFailure(Throwable caught) {
                lblMessage.setStyleName("erreur");
                lblMessage.setText("Echec de la communication");
            }
        };
        
        button.addClickListener(new ClickListener() {

            public void onClick(Widget sender) {
                // invocation du service
                getPersonneService().obtenirParId(1, callback);
                // getService().saluer(text.getText(), new MonAsyncCallback(lblMessage));
            }
        });
        
        Panel main = new FlowPanel();
        RootPanel.get().add(main);
        main.add(button);
        main.add(lblNom);
        main.add(textNom);
        main.add(lblPrenom);
        main.add(textPrenom);
        main.add(lblTaille);
        main.add(textTaille);
        main.add(lblMessage);
    }
    
    public static PersonneServiceAsync getPersonneService() {
        PersonneServiceAsync service = (PersonneServiceAsync) 
          GWT.create(PersonneService.class);
        ServiceDefTarget endpoint = (ServiceDefTarget) service;
        String moduleRelativeURL = GWT.getModuleBaseURL() + "services/personneservice";
        endpoint.setServiceEntryPoint(moduleRelativeURL);
        return service;
    }
}

Le résultat de l'application après un appui sur le bouton et la réception de la réponse du serveur est le suivant :

 

80.13.1.3. L'invocation périodique d'un service

Pour rafraichir périodiquement des données grâce à un appel serveur, il faut combiner l'utilisation d'un appel RPC et d'une instance de la classe Timer.

Dans l'exemple ci-dessous, la méthode obtenirValeur() d'un service renvoie un nombre aléatoire compris entre 0 et 1000.

Exemple :
package com.jmdoudoux.test.gwt.server.services;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.jmdoudoux.test.gwt.client.services.MonServiceGWT;

public class MonServiceGWTImpl extends RemoteServiceServlet implements
        MonServiceGWT {
    
    public int obtenirValeur() {
        double valeur = Math.random() * 1000;
        return (int) Math.round(valeur);
    }
}

Dans l'IHM de l'application, un Timer est défini : son rôle est d'invoquer toutes les secondes la méthode du service et d'afficher le résultat.

Exemple :
package com.jmdoudoux.test.gwt.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.RootPanel;
import com.jmdoudoux.test.gwt.client.services.MonServiceGWT;
import com.jmdoudoux.test.gwt.client.services.MonServiceGWTAsync;
import com.jmdoudoux.test.gwt.client.services.PersonneService;
import com.jmdoudoux.test.gwt.client.services.PersonneServiceAsync;

public class MainEntryPoint implements EntryPoint {

    private Label lblMessage = new Label();

    public MainEntryPoint() {
    }

    public void onModuleLoad() {

        Timer timer = new Timer() {

            AsyncCallback callback = new AsyncCallback() {

                public void onSuccess(Object result) {
                    lblMessage.setText(" valeur = "+ result);
                }

                public void onFailure(Throwable caught) {
                    lblMessage.setText("Echec de la communication");
                }
            };

            public void run() {
                getService().obtenirValeur(callback);
            }
        };
        timer.scheduleRepeating(1000);

        Panel main = new FlowPanel();
        RootPanel.get().add(main);
        main.add(lblMessage);
    }

    public static MonServiceGWTAsync getService() {
        MonServiceGWTAsync service = (MonServiceGWTAsync) GWT.create(MonServiceGWT.class);
        ServiceDefTarget endpoint = (ServiceDefTarget) service;
        String moduleRelativeURL = GWT.getModuleBaseURL() + "services/monservicegwt";
        endpoint.setServiceEntryPoint(moduleRelativeURL);
        return service;
    }
}

 

80.13.2. L'objet RequestBuilder

en construction
La suite de cette section sera développée dans une version future de ce document

 

80.13.3. JavaScript Object Notation (JSON)

La classe JSONObject encapsule un message au format JSON. Pour l'utiliser, il suffit de créer une instance de cette classe et d'utiliser la méthode put() pour ajouter une propriété en fournissant en paramètre son nom et sa valeur. La méthode toString() permet d'obtenir le message sous la forme d'une chaîne de caractères.

en construction
La suite de cette section sera développée dans une version future de ce document

 

80.14. La manipulation des documents XML

GWT propose un parseur XML reposant sur DOM pour permettre l'analyse ou la création de documents XML. GWT utilise le parseur du navigateur ce qui permet d'avoir de bonnes performances lors de son utilisation.

Pour utiliser les fonctionnalités de manipulation de documents XML de GWT, il faut ajouter dans la configuration du module un tag <inherits> ayant un attribut name avec la valeur « com.google.gxt.xml.XML ».

Exemple :
<?xml version="1.0" encoding="UTF-8"?>
<module>
  <inherits name="com.google.gwt.user.User"/>
  <inherits name ="com.google.gwt.xml.XML"/>
  <entry-point class="com.jmdoudoux.test.gwt.client.MainEntryPoint"/>
</module>

Pour la mise en oeuvre de l'API DOM, GWT propose plusieurs classes regroupées dans le package com.google.gwt.xml.client.

Exemple :
package com.jmdoudoux.test.gwt.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.xml.client.Document;
import com.google.gwt.xml.client.Element;
import com.google.gwt.xml.client.Node;
import com.google.gwt.xml.client.NodeList;
import com.google.gwt.xml.client.XMLParser;

public class MainEntryPoint implements EntryPoint {

    private Label lblMessage = new Label();
    private Label lblNom = new Label("Nom : ");
    private Label lblPrenom = new Label("Prénom : ");
    private Label lblTaille = new Label("Taille : ");
    private TextBox textNom = new TextBox();
    private TextBox textPrenom = new TextBox();
    private TextBox textTaille = new TextBox();
    private Button button = new Button("Afficher données");

    public MainEntryPoint() {
    }

    public void onModuleLoad() {

        button.addClickListener(new ClickListener() {

            public void onClick(Widget sender) {
                Document doc = XMLParser.parse("<personne><nom>nom1</nom>"
                  +"<prenom>prenom1</prenom>"
                  +"<taille>170</taille></personne>");
                Element root = doc.getDocumentElement();
                NodeList children = root.getChildNodes();
                for (int i = 0 ; i < children.getLength(); i++) {
                    Node node = children.item(i);
                  Window.alert("node name="+node.getNodeName()
                    +" value="+node.getFirstChild().getNodeValue());   
                }
                
                textNom.setText(children.item(0).getFirstChild().getNodeValue());
                textPrenom.setText(children.item(1).getFirstChild().getNodeValue());
                textTaille.setText(children.item(2).getFirstChild().getNodeValue());
            }
        });

        Panel main = new FlowPanel();
        RootPanel.get().add(main);
        main.add(button);
        main.add(lblNom);
        main.add(textNom);
        main.add(lblPrenom);
        main.add(textPrenom);
        main.add(lblTaille);
        main.add(textTaille);
        main.add(lblMessage);
    }
}

 

80.15. La gestion de l'historique sur le navigateur

Les applications utilisant AJAX modifient seulement les portions nécessaires d'une page sans la recharger entièrement : ce type d'applications est nommé SPI (Single Page Interface).

Il en résulte pour l'utilisateur une modification de ses habitudes avec le bouton Back du navigateur. Avec des applications web n'utilisant pas Ajax, l'utilisateur peut toujours revenir à la page précédente en utilisant ce bouton.

en construction
La suite de cette section sera développée dans une version future de ce document

 

80.16. Les tests unitaires

Un support des tests unitaires automatisés est proposé par GWT avec JUnit. La version de JUnit supportée est la 3.

Pour écrire un cas de test, il faut écrire une classe qui hérite de la classe GWTTestCase. Il faut définir la méthode getModuleName() et définir les tests en écrivant la ou les méthodes commençant par test.

GWT propose un script pour générer un fichier de tests unitaires et deux scripts pour exécuter ces tests.

Exemple :
D:\gwt-windows-1.3.3>junitCreator
-junit D:/api/junit3.8.1/junit.jar -module com.jmdoudoux.testgwt.MonApp
-out MonAppProjet 
com.jmdoudoux.testgwt.client.MonAppTests
Created
directory MonAppProjet\test\com\jmdoudoux\testgwt\client
Created file
MonAppProjet\test\com\jmdoudoux\testgwt\client\MonAppTests.java
Created
file MonAppProjet\MonAppTests-hosted.cmd
Created
file MonAppProjet\MonAppTests-web.cmd

La syntaxe de junitcreator est la suivante :

JUnitCreator -junit pathToJUnitJar -module moduleName [-eclipse projectName] 
[-out dir] [-overwrite] [-ignore] className

Le script junitcreator possède plusieurs paramètres :

Le fichier com.jmdoudoux.testgwt.client.MonAppTests.java dans le répertoire test est créé pour servir de base aux tests.

junitCreator -junit D:/api/junit3.8.1/junit.jar -module com.jmdoudoux.testgwt -eclipse MonAppProjet -out MonAppProjet com.jmdoudoux.testgwt.client.MonAppTests

Exemple :
package com.jmdoudoux.testgwt.client;
      
import com.google.gwt.junit.client.GWTTestCase;

/**
 * GWT JUnit tests must extend GWTTestCase.
 */
public class MonAppTests extends GWTTestCase {
  /**
   * Must refer to a valid module that sources this class.
   */
  public String getModuleName() {
    return "com.jmdoudoux.testgwt.monApp";
  }
  /**
   * Add as many tests as you like.
   */
  public void testSimple() {
    assertTrue(true);
  }
}

Deux scripts sont créés pour exécuter les tests unitaires :

Pour mettre en oeuvre Junit dans un module GWT, il faut :

Dans une méthode de test, il est possible de :

en construction
La suite de cette section sera développée dans une version future de ce document

 

80.17. Le déploiement d'une application

en construction
La suite de cette section sera développée dans une version future de ce document

 

80.18. Des composants tiers

Les composants fournis en standard avec GWT sont assez basiques. Pour pouvoir développer une IHM avec des composants plus riches, il est nécessaire d'utiliser une des bibliothèques tierces proposées notamment par la communauté open source.

Il existe deux formes de composants tiers :

 

80.18.1. GWT-Dnd

GWT-Dnd est une bibliothèque qui propose un support pour le drag and drop dans les applications GWT.

 

80.18.2. MyGWT

MyGWT est une bibliothèque open source de composants pour GWT.

 

80.18.3. GWT-Ext

GWT-Ext est un wrapper de la bibliothèque JavaScript Ext 2.0. Ext qui est une bibliothèque de composants JavaScript très riche proposant des composants graphiques évolués (grilles avec tri, pagination et filtre, treeview, ...)

Le site officiel du projet est à l'url http://gwt-ext.com

Une démo est consultable à l'url http://gwt-ext.com/demo/ : elle permet de visualiser toute la richesse de la bibliothèque et propose pour chaque exemple de visualiser le code source correspondant.

Produit

Version utilisée

GWT

1.4.60

GWT-Ext

2.0.1

ext

2.1

 

80.18.3.1. Installation et configuration

Pour utiliser GWT-Ext, il faut :

Ensuite, il faut copier les ressources suivantes dans le sous-répertoire ext créé précédemment :

Enfin, il est nécessaire d'ajouter la bibliothèque dans la configuration du module en ajoutant :

Exemple :
<module>

        <!-- Inherit the core Web Toolkit stuff.                  -->
        <inherits name='com.google.gwt.user.User'/>
      <inherits name='com.gwtext.GwtExt' />

        <!-- Specify the app entry point class.                   -->
        <entry-point class='com.jmdoudoux.text.gwt.ext.client.MonAppExt'/>

        <stylesheet src="js/ext/resources/css/ext-all.css" />
        <script src="js/ext/adapter/ext/ext-base.js" />
        <script src="js/ext/ext-all.js" />
</module>

 

80.18.3.2. La classe Panel

La classe Panel encapsule un panneau qui possède un titre et un contenu.

Exemple :
package com.jmdoudoux.text.gwt.ext.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.RootPanel;
import com.gwtext.client.widgets.Panel;

public class MonAppExt implements EntryPoint {

  public void onModuleLoad() {
    Panel mainPanel = new Panel() {
      {
         setTitle("Titre du panneau");
         setHeight(90);
         setWidth(200);
         setFrame(true);
         setHtml("<p>Contenu du panneau</p>");
         setStyle("margin: 10px 10px 10px 10px;");
      }
   };

   RootPanel.get().add(mainPanel); 
  }
}

 

80.18.3.3. La classe GridPanel

La classe GridPanel encapsule un panneau avec un titre et une grille de données.

Exemple :
package com.jmdoudoux.text.gwt.ext.client;

import java.util.Date;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.ui.RootPanel;
import com.gwtext.client.core.EventObject;
import com.gwtext.client.data.ArrayReader;
import com.gwtext.client.data.DateFieldDef;
import com.gwtext.client.data.FieldDef;
import com.gwtext.client.data.FloatFieldDef;
import com.gwtext.client.data.MemoryProxy;
import com.gwtext.client.data.Record;
import com.gwtext.client.data.RecordDef;
import com.gwtext.client.data.Store;
import com.gwtext.client.data.StringFieldDef;
import com.gwtext.client.widgets.Button;
import com.gwtext.client.widgets.Panel;
import com.gwtext.client.widgets.Toolbar;
import com.gwtext.client.widgets.ToolbarButton;
import com.gwtext.client.widgets.event.ButtonListenerAdapter;
import com.gwtext.client.widgets.grid.CellMetadata;
import com.gwtext.client.widgets.grid.ColumnConfig;
import com.gwtext.client.widgets.grid.ColumnModel;
import com.gwtext.client.widgets.grid.GridPanel;
import com.gwtext.client.widgets.grid.Renderer;

public class MonAppExt implements EntryPoint {

  private static final DateTimeFormat dateFormatter = DateTimeFormat.getFormat("M/d/y");
  
  public void onModuleLoad() {
    final GridPanel grille = new GridPanel();  

    Panel panneau = new Panel();  
    panneau.setBorder(false);  
    panneau.setPaddings(15);  

    RecordDef recordDef = new RecordDef(  
            new FieldDef[]{  
                    new StringFieldDef("nom"),  
                    new StringFieldDef("prenom"),  
                    new FloatFieldDef("taille"),  
                    new DateFieldDef("datenais", "d/m/Y")  
            }  
    );  

    
    Object[][] donnees =  new Object[][]{  
        new Object[]{"Nom1", "Prenom1", 
          new Double(1.75), "13/10/1965"},  
        new Object[]{"Nom2", "Prenom2", 
          new Double(1.45), "13/10/1975"},  
        new Object[]{"Nom3", "Prenom3", 
          new Double(1.67), "13/10/1972"},  
        new Object[]{"Nom4", "Prenom4", 
          new Double(1.81), "13/10/1969"},  
        new Object[]{"Nom5", "Prenom5", 
          new Double(2.05), "13/10/1961"},  
        new Object[]{"Nom6", "Prenom6",  
          new Double(1.77), "13/10/1981"}  
    };   
    MemoryProxy proxy = new MemoryProxy(donnees);  

    ArrayReader reader = new ArrayReader(recordDef);  
    Store store = new Store(proxy, reader);  
    store.load();  
    grille.setStore(store);  


    ColumnConfig[] colonnes = new ColumnConfig[]{  
            new ColumnConfig("Nom", "nom", 
              150, true, null, "nom"),  
            new ColumnConfig("Prenom", "prenom", 
              150, true, null, "prenom"),  
            new ColumnConfig("Taille", "taille", 
              50, true, new Renderer() {
                public String render(Object value, CellMetadata cellMetadata, 
                  Record record, int rowIndex, int colNum, Store store) {
                  return "<div>" + value + "m</div>";
              }}),  
            new ColumnConfig("Date de naissance", "datenais", 
              100, true, new Renderer() {
                public String render(Object value, CellMetadata cellMetadata, 
                  Record record, int rowIndex, int colNum, Store store) {
                  Date date = (Date)value;
                  return "<div>" + 
                    dateFormatter.format(date) + "</div>";
            }})  
    };  

    ColumnModel columnModel = new ColumnModel(colonnes);  
    grille.setColumnModel(columnModel);  

    grille.setFrame(true);  
    grille.setStripeRows(true);  

    grille.setHeight(250);  
    grille.setWidth(470);  
    grille.setTitle("Grille de donnees");  

    Toolbar bottomToolbar = new Toolbar();  
    bottomToolbar.addFill();  
    bottomToolbar.addButton(new ToolbarButton("Effacer tri", 
      new ButtonListenerAdapter() {  
        public void onClick(Button button, EventObject e) {  
            grille.clearSortState(true);  
        }  
    }));  
    grille.setBottomToolbar(bottomToolbar);  
    
    panneau.add(grille);  

    RootPanel.get().add(panneau); 
  }
}

Le composant offre en standard des fonctionnalités avancées comme le tri des données d'une colonne.

Le composant permet aussi de sélectionner les colonnes qui seront affichées.

 

80.19. Les ressources relatives à GWT

Le site officiel de GWT : http://www.gwtproject.org/

Le guide de démarrage : http://www.gwtproject.org/gettingstarted.html

Le guide du développeur : http://www.gwtproject.org/doc/latest/DevGuide.html

http://www.ongwt.com/ pour se tenir informé de l'actualité GWT

 


  79. AJAX Partie 12 : Le développement d'applications avec Spring Imprimer Sommaire Consulter avec table des matières Développons en Java   v 2.10  
Copyright (C) 1999-2016 .