Date de première publication : 2012/05/18
Objectif et contexte
L'objectif de cet exercice est de créer une petite application JSF à partir d'un jeu très simple : deviner un nombre entre 0 et 100.
L'environnement de développement est le suivant :
- Java : Java SE 8 et Java EE 7
- Netbeans 8.2
- Glassfish 4.1.1
- Mojarra (JSF) 2.1.6
Cette page reprend des éléments de
Création du site statique
Ce n'est pas le plus intéressant de l'histoire mais il faut bien commencer par là. Il faut créer un petit mockup de notre site web [voir]. Avec ma mégalomanie usuelle, j'ai juste remplacé Duke. On prendra la même page pour afficher la réponse, on changera le texte dans la bulle et éventuellement les boutons dans la zone de proposition.
Une fois qu'on l'a, on peut soit créer une application web à partir du modèle et convertir en jsf (ce que fait [1]), ou alors on crée un nouveau projet et on remet les choses dedans. Je choisis la dernière solution.
Création du site dynamique
Créer un nouveau projet Netbeans :
- Projet : Java Web / Web application
- Nom : Guess
- Serveur d'applications : Glassfish Server/ Java EE 7 Web
- Frameworks : JavaServer Faces (JSF 2.2)
Si l'on regarde le code généré : on a un fichier xhtml 1.0 transitionnel avec un joli espace de nommage. Le code (x)HTML est toutefois du 5.
Les ressources
Les ressources sont des données nécessaires au bon rendu de l'application. Elles se trouvent dans un emplacement standard :
un sous-répertoire de Web Pages qui s'intitule resources. Cela revient à créer le répertoire web/resources
.
Il faut bien écrire resources à l'anglaise et ne pas mettre deux s !
On peut identifier deux ressources pour ce projet
- une feuille de style
- une image (ma tête !)
Commençons par la feuille de style : créer un sous-répertoire de resources intitulé style. Ce nouveau répertoire est ce que l'on appelle une bibliothèque, on va y placer toutes les informations de style et donc guess.css
Le mécanisme de ressources est beaucoup plus fin : on peut gérer les ressources en fonction de la langue de l'application (locale), d'un numéro de version de la bibliothèque et enfin d'un numéro de version de la ressource.
Dans le code XHTML, pour accéder à la ressource, il faut taper le code suivant :
<h:outputStylesheet library="style" name="guess.css" />
Passons maintenant à l'image : créer une bibliothèque images (sous-répertoire de resources
) et y placer l'image. Il faut ajouter une des balises suivantes au code XHTML :
<h:graphicImage alt="" value="#{resources['images:loic.png']}" />
<h:graphicImage alt="" library="images" name="loic.png" />
Cela peut être une bonne idée d'exécuter l'application (MAJ+F6) pour voir ce que l'on a fait ! et d'observer le code source généré. Vous pouvez également tester avec une ressource non trouvée.
On pourrait aller plus loin et intégrer du javascript dans notre page : une balise <outputScript library="" name="" />
est à notre disposition. Il faut juste faire attention à la manière dont JSF génère les balises HTML, en particulier les id
.
Le formulaire
Je vous propose de copier le reste du fichier, en remplaçant img
par graphicImage
et de voir ce que cela donne !
En fait, l'image n'est pas à la bonne taille. Il manque un lien entre le CSS et l'image, à savoir un identifiant sur la balise graphicImage
En passant, il est possible de sélectionner tout le code et de le reformater (clic bouton droit) pour que cela soit plus propre.
Il faut maintenant que l'on s'occupe des balises formulaire form
et input
.
La balise form
est simplement préfixée par l'espace de nommage h
: h:form
.
Il y a une balise spécifique pour le bouton "submit" du formulaire
<h:commandButton value="Ok" action="index" />
L'attribut action
permet de définir l'action à réaliser lors de la soumission du formulaire. Dans cette forme, on demande de charger la page index.xhtml
qui est la page sur laquelle on est en train de travailler.
Il y a également une balise spécifique pour le champ de saisie texte
<h:inputText id="nombre" title="Un nombre de 1 à 100" size="2" >
</h:inputText >
En fait, il y a idéalement un attribut value
que l'on ajoutera plus tard.
Grâce à JSF, on peut contrôler la valeur à saisir. Essayer le code suivant :
<h:inputText id="nombre" title="Un nombre de 1 à 100" value="" size="2" >
<f:validateLongRange minimum="1" maximum="100" />
</h:inputText >
Il faut importer le bon espace de nommage (laissez Netbeans vous aider !) et testez désormais le comportement !
Testez la validation avec un champ non renseigné, un nombre dans les limites ou pas, ou quelque chose d'alphanumérique !
Logique métier
C'est bien joli tout ça mais il n'y a pas encore d'interaction avec l'utilisateur. On va s'attaquer à la "logique métier" de notre application.
Bean managé
La première chose à faire est de créer ce que l'on appelle un "bean managé", autrement dit :
- bean : un Plain Old Java Object (POJO) qui respecte certaines conventions (constructeur sans argument, getter/setter pour les attributs) et qui aura la responsabilité de tirer un nombre au hasard entre 1 et 100 :
- managé : géré par JSF
Avec l'assistant Netbeans :
- (M) File > new > JavaServer Faces > JSF Managed Bean
- Nom de la classe : LoicBean
- Package : beans
- Scope : Session
Cet objet est bien vide alors il faut ajouter quelques petites choses. On va tout d'abord s'occuper du paramétrage de la phrase affichée.
- Ajouter un attribut de type
String
phrase et son getter, à la main ou après un clic droit / insertion de code phrase
est initialisé au petit texte "Je pense à un nombre entre <strong>1</strong> et <strong>100</strong>. Pouvez-vous le trouver ?" dans le constructeur
Aller dans le fichier index.xhtml
. Remplacer le texte de la balise p
par
<p id="text">#{loicBean.phrase}</p>
Lorsque l'on a créé le bean avec l'assistant de Netbeans, on a défini le nom de l'instance pour le retrouver et les fichiers de configuration ont été mis à jour automatiquement. L'écriture du dessus fait appel au getter de l'attribut phrase de l'instance loicBean de LoicBean, l'instance elle-même créée par JSF
Si vous testez, vous constaterez que cela marche ...enfin presque... Pour éviter ce que l'on voit, il faut plutôt essayer cela :
<p id="text">
<h:outputText value="#{loicBean.phrase}" escape="false" />
</p>
C'est mieux, non ?
On va maintenant générer un nombre au hasard que l'on appellera devinette
- Ajouter un attribut entier :
devinette
. - Initialiser
devinette
dans le constructeur. Ce nombre sera initialisé à chaque nouvelle session (la durée de vie du Bean). - Pas de getter/setter car l'information ne sortira pas de l'objet
Et la proposition de l'utilisateur ?
- Ajouter un attribut entier :
utilisateur
pour la saisie utilisateur. - Ajouter le getter et le setter pour
utilisateur
.
Pour permettre la lecture du nombre, on va se servir de l'attribut value
que l'on avait mis de côté.
<h:inputText id="nombre" title="Un nombre de 1 à 100"
value="#{loicBean.utilisateur}" size="2" >
</h:inputText >
La comparaison ...
On va maintenant comparer les deux nombres utilisateur
et devinette
. Il faut ajouter une nouvelle méthode comparer()
au bean qui modifie la phrase en fonction de la comparaison. La méthode n'admet aucun argument et retourne une chaîne de caractères significative pour JSF : un nom de page web, un résultat ... Pour que le traitement se fasse, il faut adapter l'attribut action :
<h:commandButton value="Ok" action="#{loicBean.comparer()}" />
Aller plus loin ...
Je vous propose de réaliser les compléments suivants :
- Fixer les valeurs min et max de
devinette
et adapter l'affichage de la page en conséquence - Compter le nombre d'essais et éventuellement le limiter avec le message qui va bien
- Afficher l'éventuel message d'erreur à la validation dans la bulle.
- Permettre de recommencer. Afficher des informations conditionnelles peut être plus facile avec plusieurs pages, on peut aussi regarder ce que l'on peut faire avec rendered
- Internationaliser notre petite appli
- Documentation JSF
Un truc qui peut vous aider si vous voulez une nouvelle session :
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
La prochaine étape consiste à traduire notre application web en proposant l'internationalisation. C'est par là.