Cet article est le second d’une série publiée à l’origine en anglais sur mon blog personnel. J’ai déjà décrit le cœur des places GWT 2.1 et je vais maintenant me concentrer sur les fonctionnalités optionnelles.
# Confirmation par l’utilisateur avant de naviguer
Dans de nombreuses applications, vous voulez avoir la confirmation de l’utilisateur avant de naviguer ; notamment lorsque l’utilisateur fait des modifications et ne les a pas enregistrées. C’est relativement aisé avec les places GWT 2.1 : il suffit d’enregistrer un PlaceChangeRequestEvent.Handler
sur le bus d’événements et d’appeler la méthode setWarning
du PlaceChangeRequestEvent
avec une valeur non-nulle, qui sera alors utilisée comme message de demande de confirmation.
Lorsqu’on appelle goTo
sur le PlaceController, avant de changer de place courante, celui-ci commence en fait par émettre un PlaceChangeRequestEvent. Une fois l’événement dispatché, la place courante ne sera effectivement modifiée, et le PlaceChangeEvent dispatché, que dans deux cas :
- la propriété
warning
de l’événement vautnull
, ou - la propriété
warning
de l’événement n’est pas nulle, mais l’utilisateur a confirmé la navigation. Cette confirmation est demandée via une boîte de dialogue (native au navigateur), la propriétéwarning
de l’événement étant utilisée comme message présenté à l’utilisateur.
De plus, lorsque l’utilisateur quitte l’application (navigue vers un autre site, ou ferme la fenêtre ou l’onglet), un tel PlaceChangeRequestEvent est également dispatché (la prochaine place étant fixée à Place.NOWHERE
), et une valeur non-nulle pour la propriété warning
de l’événement provoquera également une demande de confirmation (sans entrer dans les détails, la propriété warning
est alors utilisée comme message
de l’événement Window.ClosingEvent
).
# Intégration avec RequestFactory
GWT 2.1 ne fournit pas d’intégration directe avec RequestFactory
. On peut cependant trouver dans l’exemple Expenses un aperçu de ce qui viendra dans une prochaine version, sous la forme de deux places liées à, respectivement, un type et une instance d’EntityProxy
: la ProxyListPlace
représente la liste des objets d’un type donné, alors que la ProxyPlace
indique une opération (création, détails, ou modification) sur une instance identifiée.
# Intégration avec l’historique du navigateur
L’intégration avec l’historique du navigateur implique de :
- sérialiser la place actuelle dans l’URL, afin qu’elle puisse être mise en signet/favori/bookmark, et créer une entrée dans l’historique de navigation du navigateur, et
- désérialiser l’URL vers une place, et dispatcher les événements appropriés sur le bus d’événements pour mettre à jour la place courante.
Le premier est utilisé lorsque la place change suite à un appel à la méthode goTo
du PlaceController, alors que le second est déclenché par le navigateur lorsque l’utilisateur soit navigue dans son historique par les boutons « page précédente » et « page suivante » du navigateur, soit charge une URL générée par le premier (à partir d’un signet, d’un lien dans une page web, un e-mail ou sa messagerie instantanée).
Pour utiliser cette fonctionnalité, vous devez d’abord créer des PlaceTokenizers pour traduire les history tokens vers / depuis des places, puis une interface (vide), descendant de PlaceHistoryMapper
et annotées avec @WithTokenizers
pour déclarer les PlaceTokenizer
s à utiliser, interface que vous instancierez avec GWT.create()
et inscrirez, via un PlaceHistoryHandler
, avec le PlaceController, le bus d’événements et une place par défaut (utilisée quand il n’y a aucun history token, ou lorsque celui-ci ne peut pas être traduit en place).
1 2 3 4 5 6 7 |
@WithTokenizers(FooPlaceTokenizer.class, BarPlaceTokenizer.class) interface MyPlaceHistoryMapper extends PlaceHistoryMapper { } PlaceHistoryMapper historyMapper = GWT.create(MyPlaceHistoryMapper.class); PlaceHistoryHandler historyHandler = new PlaceHistoryHandler(historyMapper); historyHandler.register(placeController, eventBus, defaultPlace); |
# Comment le PlaceHistoryHandler traduit les history tokens en places ?
Chaque PlaceTokenizer est associé à un préfixe grâce à une annotation @Prefix. Le PlaceHistoryHandler découpe l’history token sur le premier deux-points, compare la partie gauche aux préfixes qu’il connait, et instancie, avec GWT.create(), le PlaceTokenizer correspondant afin d’appeler sa méthode getPlace
en passant la partie droite de l’history token en argument.
# Comment le PlaceHistoryHandler traduit les places en history tokens ?
PlaceTokenizer est une classe générique (paramétrée) définie exactement de la sorte : PlaceTokenizer<P extends Place>
. Lorsque le générateur de code travaille sur le PlaceHistoryHandler, il examine les PlaceTokenizers et extrait leur argument générique P
. Cette information est ensuite utilisée à l’exécution pour associer une place à sa classe PlaceTokenizer, de sorte que le PlaceHistoryHandler puisse l’instancier avec GWT.create() et appeler sa méthode getToken. La valeur retournée est alors préfixée du @Prefix
du PlaceTokenizer et d’un deux-points comme séparateur, et le résultat utilisé finalement comme history token.
# Utiliser une fabrique pour les PlaceTokenizers
Votre PlaceTokenizer peut avoir des dépendances sur d’autres objets, mais il n’y a pas de place pour la personnalisation dans l’algorithme ci-dessus, qui crée une nouvelle instance de PlaceTokenizer chaque fois qu’il en a besoin pour l’oublier instantanément. C’est là que le PlaceHistoryHandlerWithFactory
fait son entrée : vous héritez de cette interface au lieu de PlaceHistoryHandler et appelez setFactory
avec une fabrique (ou un fournisseur ; en anglais respectivement factory et provider) de PlaceTokenizers juste avant de l’initialiser.
PlaceHistoryHandlerWithFactory a un argument de type générique, que le générateur de code utilisera pour lister toutes les méthodes sans argument dont le type de retour est assignable à PlaceTokenizer. La classe générée appelera ces méthodes au lieu d’utiliser GWT.create() quand elle aura besoin d’un PlaceTokenizer. Les méthodes peuvent également être annotées avec @Prefix pour remplacer l’annotation éventuellement présente sur la sous-classe de PlaceTokenizer. Enfin, les classes PlaceTokenizer que votre fabrique peut fournir ne doivent pas être listées dans l’annotation @WithTokenizer, qui devient donc facultative.
Votre fabrique peut vraiment être n’importe quelle classe ou interface, vous pouvez donc même utiliser un Ginjector si vous utilisez GIN pour l’injection de dépendances.
# Utiliser son propre PlaceHistoryMapper, sans PlaceTokenizer
Vous n’êtes en fait pas tenu d’utiliser GWT.create()
sur un ProxyHistoryMapper
, et donc de vous appuyer sur le code généré à base de PlaceTokenizer
s ; vous pouvez très bien implémenter l’interface vous-même, comme bon vous semble. Et non seulement c’est techniquement possible, mais c’est un cas d’utilisation explicitement reconnu (vous pouvez me croire sur ce coup, c’est moi qui ai contribué le patch).
# Et pour la suite ?
Le prochain article traitera des activités GWT 2.1, qui se situent au-dessus des places, vous libérant de la gestion des événements décrits jusque là. C’est ce que l’équipe GWT appelle le Framework MVP de GWT 2.1 même si en l’occurence, bien qu’elles aident à la mise en place du pattern MVP, elles n’obligent en rien son utilisation ; et vous aurez probablement aussi besoin de construire des composants MVP qui ne sont pas des activités, donc… Ceci étant, les activités GWT 2.1 apportent une API avec laquelle il fait vraiment bon travailler, stay tuned!
24 janvier 2011 at 14 h 24 min
Hello,
..et tout d’abord encore bravo pour cette série de posts ô combien intéressants.
J’aimerai avoir ton avis concernant le meilleur moyen de faire passer des HashMap entre serveur et client. J’ai regardé de près la Request Factory, mais j’ai l’impression qu’il n’y a aucun moyen de les y faire passer malheureusement…Utilisant AppEngine, il y avait quand même quelques avantages à utiliser RF plutôt que RPC.
Aurais tu à tout hasard connaissance d’un workaround concernant le sujet?
D’avance, merci.