GWT : Création de l’applicatif (partie client)

Notre application n’est pour l’instant peut-être pas très jolie, mais elle a l’avantage de contenir tous les conteneurs et commandes nécessaires pour recevoir les données et actions. Vous pouvez télécharger l’application en version 0.1 avant de commencer cet article.

Objet metier

Dans le projet GWTwitter, l’objet métier est le Tweet. Il doit contenir, pour le moment, un texte et une date. Par la suite nous lui rajouterons d’autres attributs. Mais pour l’instant créons cette classe dans le package « client ». Cet objet métier va circuler entre le serveur (AppEngine par exemple) et le client (un navigateur), c’est pourquoi il doit implémenter l’interface Serializable ou l’interface IsSerializable et doit avoir un constructeur sans paramètre.

IsSerializable, ajoutée par GWT, est une interface simplifiée de Serializable

package me.gwtwitter.client;
 
import java.util.Date;
 
import com.google.gwt.user.client.rpc.IsSerializable;
 
public class Tweet implements IsSerializable {
 
	private String text;
	private Date date;
 
	public Tweet() {}
 
	public Tweet(String text) {
		this.text = text;
		this.setDate(new Date());
	}
 
	public String getText() {
		return text;
	}
 
	public void setText(String text) {
		this.text = text;
	}
 
	public void setDate(Date date) {
		this.date = date;
	}
 
	public Date getDate() {
		return date;
	}
}

RemoteService

Passons maintenant au RPC (Remote Procedure Call), en français : « L’appel de procédures distantes ». En effet nous allons, dans la partie client, appeler des méthodes « serveur » comme si elles étaient du côte client. Pour être plus précis, l’interface se trouve du côté « client » et l’implémentation se trouve du côté « serveur ». Deux interfaces nous intéresse :

  • GWTwitterService qui étend com.google.gwt.user.client.rpc.RemoteService
  • GWTwitterServiceAsync qui est étroitement liée à GWTwitterService

La première est l’interface principale, c’est elle qui va définir les méthodes RPC utilisables, leurs paramètres et leurs types de retour. La deuxième correspond à la version « asynchrone » de la première. Nous la ferons se générer toute seule.

GWTwitterService

Elle étend l’interface RemoteServce (RemoteService). Nous définirons 2 méthodes :

  • boolean newTweet(String text);
  • List<Tweet> tweets();

La classe peut utiliser le décorateur @RemoteServiceRelativePath qui permet de définir un nom pour notre classe dans le mapping des servlets. (exemple : @RemoteServiceRelativePath(« twitter »)

package me.gwtwitter.client;
 
import java.util.List;
 
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
 
@RemoteServiceRelativePath("twitter")
public interface GWTwitterService extends RemoteService {
 
	void newTweet(String text);
 
	List<Tweet> tweets();
 
}

GWTwitterServiceAsync

Pensez à enregistrer la classe ci-dessus. Vous devez maintenant voir apparaître une indication qui vous dit que 2 méthodes doivent être déclarées dans GWTwitterServiceAsync.

Deux méthodes doivent être déclarées

Il vous suffit de cliquer sur l’indication et de choisir de générer la méthode.

package me.gwtwitter.client;
 
import java.util.List;
 
import com.google.gwt.user.client.rpc.AsyncCallback;
 
public interface GWTwitterServiceAsync {
 
	void newTweet(String text, AsyncCallback<Void> callback);
 
	void tweets(AsyncCallback<List<Tweet>> callback);
 
}

Un peu de code

Maintenant que nos interfaces sont prêtes, on va pouvoir les utiliser dans notre module : GWTwitter.

ServiceAsync

Le ServiceAsync a été mis en place juste ci-dessus. Pour l’utiliser, nous allons créer un attribut de type GWTwitterServiceAsync dans notre module :

public class GWTwitter implements EntryPoint {
	private final GWTwitterServiceAsync twitterService = GWT.create(GWTwitterService.class);
	/* Le reste de notre module */
}

Cet objet nous permettra d’appeler de façon asynchrone nos RPC.

RefreshTweetsTable

Créons ensuite, dans la classe GWTwitter, une méthode nommée refreshTweetsTable qui ne retourne rien et qui n’attends rien. Elle aura pour but de d’appeler la RPC créée ci-dessus puis de présenter les tweets dans la FlexTable.

Pour appeler notre RPC il faut faire utiliser le ServiceAsync créé ci-dessus : List<Tweet> twitterService.tweets(AsyncCallback<List<Tweet>> callback).

twitterService.tweets(new AsyncCallback<List<Tweet>>() {
	@Override
	public void onSuccess(List<Tweet> result) {
		// Ici tout va bien, on peut présenter les données
	}
 
	@Override
	public void onFailure(Throwable caught) {
		// Ici il y a eu une erreur
	}
});

Nous allons, pour le moment, nous occupé uniquement du onSuccess, c’est à dire de remplir la FlexTable.

Pour remplir une cellule d’une FlexTable pas besoin de l’avoir créé au paravent. Il suffit juste de définir le contenu et elle se crée toute seule (exemple ci-dessous).

FlexTable tweetsTable = new FlexTable();
tweetsTable.setText(1, 2, "Un petit text");
tweetsTable.setWidget(1, 1, new Button("Un bouton"));

On boucle sur les txeets récupérés via la RPC :

int i = 0;
for(Tweet t : result) {
	tweetsTable.setText(++i, 0, DateTimeFormat.getMediumDateTimeFormat().format(t.getDate()));
	tweetsTable.setText(i, 1, t.getText());
}

Puis on affiche la date du moment afin d’indiquer la dernière mise à jour des données.

updateLabel.setText(DateTimeFormat.getMediumDateTimeFormat().format(new Date()));

Le tour est joué !

SubmitNewTweet

Maintenant que nous sommes capable d’afficher la liste nous tombons face à un énorme problème… Impossible de tester car il n’y a aucun Tweet de créé pour le moment.

Le principe est simple. Nous allons tout d’abord créer la méthode qui récupèrera le texte du nouveau Tweet puis l’enverra au serveur via la seconde RPC : void twitterService.writeTweet(AsyncCallback<Void> callback).

Pas besoin de tout vous expliquer à nouveau. Ici nous appliquons le même principe qu’avec l’autre RPC vue ci-dessus.

private void submitNewTweet(String text){
	twitterService.newTweet(text, new AsyncCallback() {
 
		@Override
		public void onSuccess(Boolean result) {
			tweetTextBox.setText("");
			refreshTweetsTable();
		}
 
		@Override
		public void onFailure(Throwable caught) {
			// Ici il y a eu une erreur
		}
	});
}

ClickHandler et KeyPressHandler

ClickHandler et KeyPressHandler sont deux interfaces. La première permet de réagir au clic de la souris, la deuxième à une touche appuyée (la touche « entrée » dans notre cas).

Nous allons créer une classe interne qui implémentera les deux interfaces et donc qui réagira dans les deux cas présentés ci-dessus. Cette aura deux méthodes (onClick et onKeyPress) qui appelleront toutes les deux la méthode créée précédemment (submitNewTweet).

Une classe interne est une classe à l’intérieur d’une autre classe. On s’en sert en général lorsque cette classe interne n’a pas de raison d’exister hors de la classe qui la contient.

public class GWTwitter implements EntryPoint {
	// Ici il y a tout le reste de notre classe.
	// Je ne l'affiche pas pour plus de clarté.
 
	private class MyHandler implements ClickHandler, KeyPressHandler {
 
		@Override
		public void onClick(ClickEvent event) {
			submitNewTweet(tweetTextBox.getText());
		}
 
		@Override
		public void onKeyPress(KeyPressEvent event) {
			if(event.getCharCode() == 13){
				submitNewTweet(tweetTextBox.getText());
			}
		}
 
	}
}

Pour finir avec ce Handler, nous devons l’associer à notre champ texte et à notre bouton :

public void onModuleLoad() {
	MyHandler myHandler = new MyHandler();
	submitButton.addClickHandler(myHandler);
	tweetTextBox.addKeyPressHandler(myHandler);
 
	/* Le reste de onModuleLoad() */
}

Nous voilà arrivé à la fin de cette partie. Nous avons mis en place toute (ou presque toute) la partie client de notre application. Il ne nous reste qu’à mettre en place le code du côté serveur pour avoir un projet qui fonctionne.

Vous pouvez télécharger la Version de développement 0.2 de GTWitter pour tester le résultat chez vous.

Prochaine partie : Création de l’applicatif (partie serveur).

Tagués avec : , , , , , ,
Publié dans Google Web Toolkit

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

*