Cet article est le premier d’une série publiée à l’origine en anglais sur mon blog personnel. Ray Ryan a fait le buzz l’an dernier à Google I/O 2009 avec sa présentation des Meilleures Pratiques pour l’Architecture de votre Application GWT (Google Web Toolkit Architecture: Best Practices For Architecting Your GWT App). Mais ce qui a peut-être fait le plus de bruit était sans doute moins ce qui a été dit que ce qui n’a été que brièvement évoqué : le Place Service. Depuis lors, de nombreux développeurs (moi y compris) se sont essayés au concept, mais Google l’a finalement intégré dans GWT 2.1, sorti récemment.
# Qu’est-ce qu’un lieu ?
Dans n’importe quelle application, les applications web en particulier, qui n’ont que rarement recours au concept de fenêtre (et si vous voulez mon avis, les développeurs d’applications web devraient s’efforcer de faire de ce « rarement » dans un « jamais »), on se situe par une notion de lieu (place en anglais): « où suis-je ? », « aller à … », etc. (d’ailleurs, j’ai bien utilisé le verbe « se situer », et on utilise également couramment « s’orienter », « se diriger », ou encore « naviguer »).
Des exemples de lieux incluent : votre boîte de réception, les résultats de recherche pour « GWT Places », le profil de l’utilisateur « t.broyer », la note de frais référencée « abcdefgh », etc.
L’idée est donc d’utiliser cette notion de lieu pour gérer la navigation dans l’application, en se basant sur des événements de « changement de lieu ».
Le PlaceService ici sert de pont entre ces événements et la gestion d’historique du navigateur (ceci mériterait un article complet, mais disons qu’il s’agit de rendre effectifs les boutons « page précédente » et « page suivante » du navigateur, tout en conservant l’application sur une unique page web). Le PlaceService est, et doit rester, le seul composant de l’application qui gère l’historique du navigateur et réagit à ses changements.
# Démystifier les idées fausses
Avant d’entrer dans les détails, mettons certaines choses au clair : les lieux n’ont rien à voir avec le pattern MVP, et rien à voir avec la notion d’un bus d’événements global à l’application (qui n’a lui non plus rien à voir avec MVP). Vous pouvez utiliser chacun d’eux de manière totalement indépendante. Il devient monnaie courante de tous les utiliser en même temps, mais chacun répond à des besoins différents. Cela signifie également que vous n’avez pas besoin de comprendre ces autres concepts dont Ray Ryan a parlé dans sa présentation (MVP, bus d’événements, command-pattern pour les RPC, etc.) afin de comprendre les places.
# Alors à quoi cela ressemble-t-il dans GWT 2.1?
GWT 2.1 matérialise le PlaceService à deux niveaux d’abstraction (lieux et activités, places et activities), et l’intégration optionnelle avec d’autres fonctionnalités de GWT (History pour l’instant, et RequestFactory à venir dans une prochaine version), sous la forme des modules com.google.gwt.place.Place
et com.google.gwt.activity.Activity
respectivement.
Le cœur des places GWT 2.1 est le PlaceController, que vous utiliserez pour naviguer de lieu en lieu, mais nous allons commencer par examiner ce à quoi une place ressemble dans GWT 2.1 et ce qu’elle représente.
# Qu’est-ce qu’une place GWT 2.1?
Dans GWT 2.1, les places sont des objets légers qui étendent la classe abstraite Place
. Elles sont généralement jetables et immuable, même si cela n’est pas obligatoire ; et il y a quelques bonnes raisons d’utiliser des places non jetables, comme nous le verrons plus tard (je ne vois par contre aucune raison pour avoir des places modifiable).
Vous pouvez définir autant de sous-classes de Place
que nécessaire, associées ou non à des « données » (l’identifiant d’un enregistrement, la requête d’une recherche, etc), et vous créerez une nouvelle instance à chaque fois que vous naviguerez dans votre application. La classe Place ne définit aucune méthode, mais les sous-classes doivent implémenter correctement les méthodes Object.equals(Object)
et Object.hashCode()
.
# Présentation du PlaceController
Le PlaceController
est un objet qui gère la place actuelle (où vous êtes) et la navigation entre les places. Vous ne devriez donc jamais avoir plus d’une instance PlaceController par application.
Pour permettre à votre application de réagir à des changements de lieux (pour mettre à jour l’interface utilisateur et l’état de l’application de sorte qu’elle reflète la nouvelle place), le PlaceControler déclenche des événements sur un bus d’événements. Vous n’avez pas besoin de vraiment comprendre la notion de bus d’événements pour poursuivre la lecture ; disons simplement que, au lieu d’enregistrer vos gestionnaires d’événements sur le PlaceController lui-même, vous les ajouterez à un autre objet (le bus d’événements, dont soit dit en passant il ne devrait également y avoir qu’une unique instance par application), et vous initialiserez le PlaceController afin qu’il lance ses événements sur ce bus.
Le point d’entrée de votre application contiendra donc généralement ce genre de code :
1 2 |
EventBus eventBus = new SimpleEventBus(); PlaceController placeController = new PlaceController(eventBus); |
Par défaut, le PlaceController est initialisé avec pour place actuelle Place.NOWHERE
(en d’autres termes, placeController.getWhere()
retourne Place.NOWHERE
). Pour naviguer vers un autre endroit, vous passerez une place à la méthode goTo
du PlaceController, par exemple :
1 |
placeController.goTo(new MyPlace()); |
Cela aura pour effet de modifier l’emplacement actuel, puis de lancer un PlaceChangeEvent
sur le bus d’événements, événement que vos composants vont écouter en enregistrant un PlaceChangeEvent.Handler
:
1 2 3 4 5 6 |
eventBus.addHandler(PlaceChangeEvent.TYPE, new PlaceChangeEvent.Handler() { public void onPlaceChange (PlaceChangeEvent event) { Place newPlace = event.getNewPlace(); ... } }); |
# Où aller maintenant?
Ce n’était qu’un aperçu du cœur des places GWT 2.1, afin que vous puissiez saisir les concepts sous-jacents.
Je parlerai de la confirmation par l’utilisateur de la navigation, de l’intégration avec l’historique du navigateur (ce qui signifie aussi des places enregistrables en tant que favoris/bookmarks dans le navigateur), et des activités (une API de plus haut niveau qui, entre autres, aide à l’utilisation du pattern MVP) dans les prochains articles.
23 février 2011 at 10 h 45 min
Bonjour,
J’ai lu la totalité de vos articles sur votre blog concernant le modèle mvp avec activity et places gwt 2.1 et je les trouvent passionnants. je suis en train de développer une application web pour smartPhone version light et mobile de notre application de contrôle parental pour PC (ww.parentsdanslesparages.com).
Cette application se comporte comme une application iphone, c’est à dire un menu de navigation « iphone like », avec des effets de transition latéral. Donc plusieurs niveau de menus : choix de l’utilisateur, puis choix de l’action à effectuer sur l’utilisateur, puis page de paramétrage ou de visualisation par exemple « deconnecter cet utilisateur ». Il y a également un header, qui gère le titre courant de la page et le boutons de navigation « back ».
Pour le moment afin de me familiarisé avec le concept de D’activity et de places, j’ai fait de la sorte :
Deux display régions, le header et le menu principal.
Le login est une activité, qui se loge dans le display menu principal, pas de header visible au moment du login
Une fois loggué on change de Place « menu », a cette place sont associés deux activité MenuHeader et MainMenu :
Donc MenuHeader c’est « naviguer avec le menu header » et MainMenu c’est naviguer avec le menuPrincipal »
Bon mais bien sûr cette répartition ne me satisfait pas, car il parait evident que les activités devrait être :
« Choisir un utilisateur », « Choisir une action », « Paramétrer l’action » et ainsi pouvoir bénéficier de la navigation avec les Places
associés.
Dans mon cas la difficulté provient de la vue qui est un Widget qui sait affiché des pages (model) et qui maitrise les effets de transition entre les pages. L’idée serait de transmettre la Vue et son contexte d’activité en activité. Je ne vois pas très bien
comment faire et si c’est pertinent ? Pouvez vous m’éclairer de vos lumières ?
Merci
Yann MARESCHAL