Navigation Rules avec PrimeFaces... Mobile

Tiens, allez, c’est Noël après tout : encore un tutoriel technique de derrière les fagots (Noël – cheminée – fagots… tout le monde suit je pense).

Les règles de navigation de JSF 2 sont ma foi très pratiques pour découpler l’implémentation physique des vues de la logique “métier”. Vos méthodes d’action peuvent renvoyer un mot-clef, l’outcome, interprété par le NavigationHandler à partir de déclarations externalisées dans le fichier faces-config.xml standard. Exemple, pour rappel :

``` ```
``` /* dans BusinessBean.java */ public void doSomething() { try { ... return "success"; // outcome } catch (Exception) { return "error"; // outcome } }```
``` /views/add.xhtml success /views/next.xhtml error /views/errors.xhtml ```
La logique ci-dessus indique, vous le voyez, que lorsque le résultat de l’action est “success” vous serez redirigé vers la vue “next”, tandis que vous seriez redirigé vers la vue “errors” si le résultat était “error”. Ainsi votre action est découplée à la fois de la réalité “physique” de l’implémentation des vues, et de la logique de navigation. En effet, il sera possible de modifier l’emplacement des vues visées et d’opter pour des redirections différentes selon que l’action sera appelée depuis un emplacement ou un autre (si *doSomething* était appelé depuis *next.xhtml*, alors *to-view-id* pourrait pointer vers *end.xhtml* pour l’*outcome* “success”, par exemple). Bref.

Si vous utilisez PrimeFaces Mobile, vous allez définir des vues multiples, internes aux vues JSF classiques. Sous forme de pages. Cela peut prendre la forme suivante :

``` #{todo.nom} #{todo.description}

RIEN NE VA PLUS, ICI ! ```
Vous constatez que PrimeFaces Mobile emploie une syntaxe particulière d’*outcome*, avec un préfixe “#” (commandLink) ou “pm:” (commandButton). Pour que la boucle soit bouclée, les méthodes d’action doivent retourner des *outcomes* préfixés par “pm:”, et le tour sera joué. Tout ceci grâce à la classe **MobileNavigationHandler** introduite par l’API qui fait comme ceci :
``` public void handleNavigation(FacesContext context, String fromAction, String outcome) { if (outcome != null && outcome.startsWith("pm:")) { String command = MobileUtils.buildNavigation(outcome); // montage d'une expression jQuery RequestContext requestContext = RequestContext.getCurrentInstance(); if (requestContext != null) { requestContext.execute(command.toString()); // exécution } } else { base.handleNavigation(context, fromAction, outcome); } }```
Malheureusement, et c’est là que je voulais en venir, les règles de navigation ne sont pas prises en compte par cette API. Vous voyez ci-dessus que seul l’*outcome* est considéré, et que lorsque le préfixe est détecté alors les règles de navigation sont purement et simplement ignorées. C’est *ben plate*, comme on dit à Montréal. Concrètement, il n’est pas possible d’écrire ceci nativement :
``` /mobile/main.xhtml success #accueil ```
Personnellement, je trouve ça navrant. Parce que cela nous force potentiellement à surcharger ou (pire) à recoder des *beans*, juste pour renvoyer les bons *outcomes*. C’est franchement la *loose* quand on veut avoir une couche vue *web* ET une couche vue *mobile*, sans toucher à la couche des *managed beans* (ce qui est la moindre des choses à mon avis). Mais au lieu de râler et de me lamenter, j’ai cherché une solution de contournement. Et ben j’ai trouvé (c’est Noël après tout, non ?). Voilà, c’est cadeau :
``` /** * Gestionnaire de navigation pour autoriser les Navigation Rules de type "PrimeFaces Mobile" */ public class MobileNavigationRulesHandler extends ConfigurableNavigationHandler { ... /** * Interception des règles de navigation où la destination (to view id) commence * par "#" ou "pm:" ; traitement jQuery comme pour les outcomes PrimeFaces Mobile */ @Override public void handleNavigation(FacesContext context, String fromAction, String outcome) { NavigationCase navCase = base.getNavigationCase(context, fromAction, outcome); if (navCase != null) { String toViewId = navCase.getToViewId(context); // attention, un "/" est ajouté automatiquement if (toViewId != null && (toViewId.startsWith("/#") || toViewId.startsWith("/pm:"))) { String command = MobileUtils.buildNavigation(toViewId.substring(1)); RequestContext requestContext = RequestContext.getCurrentInstance(); requestContext.execute(command.toString()); return; } } base.handleNavigation(context, fromAction, outcome); } ...```
``` /mobile/main.xhtml success #accueil /mobile/main.xhtml error pm:erreurs ```
Vous remarquerez que nous n’étendons pas la classe MobileNavigationHandler, ce serait contraire au pattern Délégation ici en oeuvre. Cependant, nous avons une dépendance à MobileUtils. Après tout, “it’s all about PrimeFaces Mobile” ; cette classe n’a pas d’intérêt sans lui, c’est donc justifié. Il ne reste plus qu’à enregistrer notre gestionnaire de navigation pour l’application dans faces-config.xml, nos méthodes d’action pourront continuer à renvoyer leurs *outcomes* agnostiques, ce sont les règles de navigation qui continueront à décider sereinement de la marche à suivre. Ce sera plus tant mieux !

Et Bonne Année à tous !