http://www.labo-sun.com [email protected] JSF FRAMEWORK WEB Auteur : Olivier Smedile Version n° 1.0 – 30 octobre 2006 Nombre de pages : 108 Ecole Supérieure d’Informatique de Paris 23. rue Château Landon 75010 – PARIS www.supinfo.com Table des matières 1. INTRODUCTION.................................................................................................................................................6 1.1. LIMITATIONS DU COUPLE SERVLET/JSP .........................................................................................................6 1.2. PRESENTATION DE JAVA SERVER FACES ........................................................................................................6 1.2.1. Public visé ................................................................................................................................................7 1.3. EVOLUTION DE JSF ..........................................................................................................................................7 1.4. DIFFERENCE AVEC STRUTS ..............................................................................................................................8 2. ARCHITECTURE DE JSF .................................................................................................................................9 2.1. INTEGRATION DE JSF DANS L’ARCHITECTURE D’UNE APPLICATION JEE....................................................10 2.2. CYCLE DE VIE .................................................................................................................................................11 2.2.1. Restauration de la vue ...........................................................................................................................12 2.2.2. Application des paramètres de la requête............................................................................................12 2.2.3. Validation des entrées utilisateurs........................................................................................................12 2.2.4. Mise à jour des valeurs du modèle .......................................................................................................12 2.2.5. Invocation de l’application ...................................................................................................................12 2.2.6. Rendu de la réponse ..............................................................................................................................12 3. DEVELOPPER EN JSF.....................................................................................................................................13 3.1. INSTALLATION DE JSF ...................................................................................................................................13 3.2. ACTIVATION DE JSF.......................................................................................................................................14 3.3. PAGES JSF ......................................................................................................................................................15 3.4. RESSOURCES NECESSAIRES ............................................................................................................................15 3.5. DEPLOIEMENT ................................................................................................................................................16 3.5.1. JBoss.......................................................................................................................................................16 3.6. EXEMPLE ........................................................................................................................................................16 4. LES BRIQUES DE BASE..................................................................................................................................21 4.1. LES COMPORTEMENTS....................................................................................................................................22 4.2. LES COMPOSANTS GENERIQUES .....................................................................................................................23 4.2.1. L’attribut id ............................................................................................................................................24 4.2.2. L’attribut renderer.................................................................................................................................24 4.2.3. L’attribut immediate ..............................................................................................................................24 4.3. LES COMPOSANTS HTML ..............................................................................................................................24 4.3.1. Style CSS ................................................................................................................................................24 4.3.2. Classe CSS .............................................................................................................................................25 4.3.3. Escape.....................................................................................................................................................25 4.3.4. Evénements JavaScript..........................................................................................................................25 4.4. LIAISON COMPOSANTS ET JAVABEANS .........................................................................................................25 4.4.1. Valeur .....................................................................................................................................................26 4.4.2. Instance ..................................................................................................................................................26 4.5. UNIFIED EXPRESSION LANGUAGE .................................................................................................................27 4.5.1. Variables ................................................................................................................................................27 4.5.2. Objets implicites ....................................................................................................................................28 4.5.3. Opérateurs..............................................................................................................................................29 5. JSF TAG-LIB ......................................................................................................................................................30 5.1. VUE JSF..........................................................................................................................................................30 5.2. TEXTE SIMPLE.................................................................................................................................................30 5.3. LES COMPOSANTS DE SORTIES .......................................................................................................................30 5.3.1. Texte simple............................................................................................................................................31 5.3.2. Message formaté ....................................................................................................................................31 5.3.3. Label .......................................................................................................................................................32 5.4. FORMULAIRE ..................................................................................................................................................32 5.5. LES COMPOSANTS D’ENTREE .........................................................................................................................33 5.5.1. Champ texte............................................................................................................................................33 5.5.2. Champ texte sur plusieurs lignes ..........................................................................................................33 JSF - Framework Web 3 / 108 5.5.3. Champ de mot de passe .........................................................................................................................33 5.5.4. Champ caché..........................................................................................................................................34 5.6. LIEN ................................................................................................................................................................34 5.6.1. Lien simple .............................................................................................................................................34 5.6.2. Lien JSF..................................................................................................................................................35 5.6.3. Paramètres .............................................................................................................................................36 5.7. BOUTON ..........................................................................................................................................................37 5.8. CHECKBOX .....................................................................................................................................................37 5.8.1. Checkbox simple ....................................................................................................................................37 5.8.2. Checkbox multiples................................................................................................................................37 5.9. RADIOBUTTON ...............................................................................................................................................41 5.10. LISTE.............................................................................................................................................................41 5.11. COMBO BOX .................................................................................................................................................42 5.12. IMAGE ...........................................................................................................................................................42 5.13. PANEL ...........................................................................................................................................................43 5.14. TABLEAU ......................................................................................................................................................43 5.14.1. Tableau statique : PanelGrid..............................................................................................................44 5.14.2. Tableau dynamique : DataTable ........................................................................................................46 5.15. MESSAGE D’ ERREUR ....................................................................................................................................49 6. CONFIGURATION SERVEUR.......................................................................................................................52 6.1. DECLARATION DES JAVABEANS ....................................................................................................................52 6.1.1. Déclaration des List...............................................................................................................................53 6.1.2. Déclaration des Maps............................................................................................................................53 6.1.3. Exemple : Déclaration d'une liste de SelectItem .................................................................................54 6.2. NAVIGATION...................................................................................................................................................56 6.2.1. Mapping global......................................................................................................................................57 6.2.2. Chaînes d’action ....................................................................................................................................57 6.2.3. Exemple ..................................................................................................................................................59 6.3. PARAMETRES DE CONTEXTE ..........................................................................................................................64 7. LES COMPORTEMENTS................................................................................................................................65 7.1. ACTIONLISTENER ...........................................................................................................................................65 7.1.1. Méthode générique ................................................................................................................................65 7.1.2. Interface ActionListener ........................................................................................................................65 7.2. VALUECHANGE LISTENER .............................................................................................................................66 7.2.1. Méthode générique ................................................................................................................................66 7.2.2. Interface ValueChangeListener ............................................................................................................66 7.3. CONVERSIONS DE DONNEES ...........................................................................................................................67 7.3.1. Conversion standard : Date ..................................................................................................................67 7.3.2. Conversion standard: Number..............................................................................................................68 7.3.3. Convertisseur personnalisé...................................................................................................................69 7.3.4. Convertisseurs pour une Liste d’éléments ...........................................................................................70 7.4. VALIDATION ...................................................................................................................................................74 7.4.1. Validateur personnalisé ........................................................................................................................75 7.4.2. Exemple : Validateur d’email ...............................................................................................................76 8. CONCEPTS AVANCES ....................................................................................................................................78 8.1. INTERNATIONALISATION ................................................................................................................................78 8.1.1. Internationalisation dynamique ............................................................................................................79 8.1.2. Encodage................................................................................................................................................79 8.1.3. Exemple : Bouton de changement de langue .......................................................................................79 8.2. LA GESTION DES MESSAGES ...........................................................................................................................81 8.2.1. Refaire les messages..............................................................................................................................83 8.3. INTEGRATION JSF ET APPLICATION JEE .......................................................................................................84 8.3.1. Injection de ressource............................................................................................................................84 8.3.2. Injection d’EJB ......................................................................................................................................84 8.3.3. Injection de WebService ........................................................................................................................85 http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 3 JSF - Framework Web 8.3.4. 9. 4 / 108 Injection d’unité de persistance ............................................................................................................85 FACELETS ..........................................................................................................................................................86 9.1. INTRODUCTION ...............................................................................................................................................86 9.2. CONFIGURATION ............................................................................................................................................87 9.2.1. Installation .............................................................................................................................................87 9.2.2. Structure d’un projet .............................................................................................................................87 9.2.3. Configuration .........................................................................................................................................87 9.3. CONCEPTS DE BASE ........................................................................................................................................88 9.3.1. Structure d’une page .............................................................................................................................88 9.3.2. JSFC .......................................................................................................................................................89 9.3.3. Template .................................................................................................................................................90 9.4. TEMPLATING DE VUE .....................................................................................................................................92 9.5. BALISES FACELETS ........................................................................................................................................96 9.5.1. Composition ...........................................................................................................................................96 9.5.2. Insert.......................................................................................................................................................98 9.5.3. Define .....................................................................................................................................................98 9.5.4. Decoration..............................................................................................................................................98 9.5.5. Include ..................................................................................................................................................100 9.5.6. Param ...................................................................................................................................................100 9.5.7. Remove .................................................................................................................................................100 9.5.8. Component ...........................................................................................................................................101 9.5.9. Fragment ..............................................................................................................................................101 9.5.10. Balises JSTL et Fonctions .................................................................................................................101 9.6. TEMPLATING DE COMPOSANTS ....................................................................................................................102 9.6.1. Le problème..........................................................................................................................................102 9.6.2. La solution avec Facelets....................................................................................................................102 9.6.3. TagFile .................................................................................................................................................103 9.7. PARAMETRES ................................................................................................................................................106 10. BIBLIOGRAPHIE..........................................................................................................................................108 http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 4 JSF - Framework Web http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 5 / 108 5 JSF - Framework Web 6 / 108 1. Introduction 1.1. Limitations du couple Servlet/JSP La première étape pour apprendre à faire du Web en Java est l’apprentissage des Servlet/JSP. Il est possible de faire un site avec uniquement des Servlet et/ou des JSP, mais il devient vite évident qu’il est plus simple d’utiliser les JSP pour faire de la présentation et des Servlets pour faire du traitement. Cependant, le couple Servlet/JSP possède plusieurs limitations : • La création de balises personnalisées est assez lourde, • Il n’existe pas de balises de haut niveau en JSP, • Il n’est pas possible de récupérer facilement les entrées utilisateurs, • Il n’existe pas de guide qui indique comment développer en Servlet/JSP. Ainsi chaque développeur peut organiser son site comme il veut. Ceci rend la maintenance difficile. 1.2. Présentation de Java Server Faces Java Server Faces (JSF) est un framework Web. Il fournit des composants de haut niveau pour créer rapidement un site complexe. Il sert également à faciliter toutes les tâches courantes liées au développement d’un site Web, validation des données, gestion de la navigation, .... JSF s’appuie sur le modèle MVC (modèle-vue-contrôleur) pour l’architecture du site et apporte une notion de composants similaires à ce qu’on peut trouver dans Swing. Ce principe de composants va permettre la mise en place des écouteurs (clic sur bouton, clic sur un lien, ...) du côté serveur. JSF offre un mapping HTML/Objet, c’est-à-dire qu’un composant HTML va correspondre à un composant JSF. Cela permet de travailler en Java avec des JavaBeans ou d’utiliser des balises et du code JSP. Les 2 méthodes de travail étant bien sûr tout à fait combinables. Lorsqu’un utilisateur va visiter une page, les composants JSF vont être transformés en HTML et inversement lorsqu’un serveur reçoit une requête utilisateur. Tout cela, toujours grâce au mapping HTML/Objet. Dernier concept intéressant de JSF, c’est que toute la navigation d’un site est définie dans un unique fichier, appelé fichier de navigation. Ceci permet de centraliser les règles de navigations et d’avoir une vue directe du plan du site. JSF fournit les services suivants: • Offre un mapping HTML/Objet (Composants JSF) • Sauvegarde l’état des composants (côté serveur) entre les requêtes, • Transforme les composants JSF en HTML en fonction du navigateur, • Gère des événements (clic sur bouton, modification d’un champ, ...) côté serveur, • Instancie et attache des objets à la session, à la requête et à l’application, • Extensibilité, de nombreux composants JSF sont disponibles. Cela va du composant calendrier, à l’arbre, en passant par le tableau triable et la barre de progression. • Supporte un système de plugin qui permet de modifier son comportement http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 6 JSF - Framework Web 7 / 108 • Gère la conversion des données (transforme une date en un champ texte, un objet métier en une chaîne et inversement, ...) , • Valide les requêtes utilisateurs (test de la longueur d’un champ, du type de contenu), • Gère les erreurs et exceptions et fournit des messages d’erreurs clairs, • Gère la navigation en fonction d’événements et d’interaction avec un modèle de données, • Gère l’internationalisation et la localisation du site. 1.2.1. Public visé JSF a été conçu pour toucher un public large. Du développeur d’application au designer, tout le monde peut utiliser JSF. Il existe 3 catégories principales de personnes parmi 5 qui peuvent utiliser JSF. • Les architectes d’applications qui vont concevoir l’application, définir le plan du site, les classes qu’il faut utiliser pour lier l’interface graphique avec la couche service de l’application, • Les développeurs d’applications qui vont implémenter toute la mécanique du site, • Les créateurs de pages qui vont eux travailler avec du HTML et des balises JSF. Il peut s’agir de développeurs ou de designers, Les 2 dernières catégories concernent les personnes qui désirent étendre la capacité de JSF. Il s’agit : • Des personnes désirant créer une interface graphique à l’aide de composants, • Des éditeurs d’IDE qui vont pouvoir se servir de JSF comme une base qu’ils vont pouvoir étendre grâce à des composants plus évolués. C’est le cas de Sun qui a créé Sun Java Studio Creator, un IDE pour faire du JSF. 1.3. Evolution de JSF JSF est une spécification proposée au JCP (Java Community Process : www.jcp.org). Ce qui veut dire qu’il n’existe pas une seule implémentation de JSF, mais un document qui décrit comment doit fonctionner JSF. Les spécifications officielles de JSF peuvent être récupérées aux adresses suivantes : http://jcp.org/en/jsr/detail?id=127 pour JSF 1.0 et http://jcp.org/en/jsr/detail?id=252 pour JSF 1.2. Sun fournit une implémentation de référence pour chaque version. Apache propose une implémentation de JSF nommée « MyFaces », celle-ci est actuellement en version 1.1. Les versions 1.0 et 1.1 de JSF sont sorties sans faire partir de J2EE 1.4. Ce n’est qu’à partir de JEE 5 que la version 1.2 de JSF est devenu un standard. JSF 1.2 est basé sur les librairies suivantes : • JSP version 2.1, • Servlet 2.5, • JSE 5.0, • JEE 5.0, • JavaBeans 1.0.1, http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 7 JSF - Framework Web • 8 / 108 JSTL 1.2. Tous les serveurs supportant JEE 5 supportent JSF version 1.2. Si votre serveur ne supporte pas JEE 5 ce n’est pas très grave. JSF 1.1 tourne sur un serveur de Servlet/JSP 2.3/1.2 et JSF 1.2 sur un serveur de Servlet/JSP 2.5/2.1. De manière générale, pour permettre à un serveur non JEE 5.0 de faire tourner JSF, il faut inclure les librairies relatives à JSF dans vos applications Web. Cependant chaque serveur possède sa propre configuration et il faut consulter la documentation associée avant de déployer une application. Ce point sera détaillé dans le chapitre « 3.5 Déploiement ». Ce cours est valable pour JSF 1.1 et 1.2. Les quelques éléments qui sont spécifiques à JSF 1.2 seront précisés. 1.4. Différence avec Struts Pour ceux qui connaissent Struts, JSF peut fortement y ressembler. Ils ont tous les deux ont été conçus pour dépasser les limites des Servlet/JSP, ils gèrent: • Les JavaBeans managés, c’est-à-dire que leur cycle de vie est géré par le conteneur, • La validation de formulaire, • La navigation flexible définie dans un seul fichier. JSF ajoute le support d’événements et l’indépendance vis-à-vis du système de rendu. Les pages JSF peuvent très bien générer du code XML, SVG, … Mais outre les possibilités, ils ont également une approche différente dans leur manière de fonctionner. Struts s’apparente à un modèle action – réponse. L’accès à une page Struts requiert le passage par une mini Servlet (classe d’action) qui va permettre d’effectuer les traitements et définir la page JSP de destination suite à une requête. En JSF, on accède directement à une page et c’est cette page qui peut choisir d’utiliser des JavaBeans pour effectuer ses tâches. Ce modèle est plus souple car il permet de vraiment réutiliser son code grâce au principe de composants. Enfin JSF est un standard qui est intégré à JEE 5 ce qui lui assure un suivi constant de la part de la communauté Java et des grandes entreprises (Oracle avec ADF/MyFaces, Sun avec Java Studio Creator 2, ...). http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 8 JSF - Framework Web 9 / 108 2. Architecture de JSF On peut voir JSF comme un Swing adapté pour le Web. En effet JSF se base sur un système de composant pour représenter la structure d’une page. Les composants fournissent des méthodes pour gérer des évènements, la validation des entrées utilisateurs et la conversion de données. Tous les composants existent sous forme de classe Java, c’est-à-dire qu’il est possible de manipuler les composants dans du code Java. Mais dans la pratique, on manipule ces composants à l’aide de balises JSF. JSF peut s’utiliser avec un autre système de balises telles que HTML ou JSP mais nous verrons dans le chapitre « 9 Facelets » un autre jeu de balise plus pratique à utiliser avec JSF. Le système de composant de JSF fournit les éléments suivants: • Interaction avec des JavaBeans. Les JavaBeans représentent l’état des éléments à afficher et permettent d’interagir avec les couches plus basses de l’application, • Un système de rendu (Renderer) qui permet de redéfinir comment sera affichée (traduit en code HTML) une balise JSF, • Un système d’événements et d’écouteurs pour effectuer des traitements en fonction des actions des utilisateurs, • Un modèle de conversion qui permet de convertir des types de données, • Un système de validation qui permet de vérifier les entrées utilisateurs. Tous ces services sont manipulés à l’aide d’attributs de balises JSF ce qui facilite grandement la création de sites. Dans la pratique le composant est invisible, c’est comme si on associait directement la balise JSF avec un JavaBean. Par rapport au modèle MVC, les composants JSF font partie de la vue et la partie modèle est représentée par des JavaBeans. Le rôle du contrôleur est joué par la Servlet de JSF. http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 9 JSF - Framework Web 10 / 108 De plus JSF centralise dans un seul fichier de configuration : • La navigation du site, • La déclaration des JavaBeans utilisés. La centralisation de la navigation permet de retrouver l’ensemble du plan du site à un seul endroit et de visualiser directement le cheminement des requêtes utilisateurs. De plus il est possible de faire de la navigation conditionnelle. Les JavaBeans qui sont déclarés dans le fichier de configuration sont dits « beans managés ». C’est le serveur qui s’occupe de les instancier pour les rendre accessible dans les pages JSF. 2.1. Intégration de JSF dans l’architecture d’une application JEE Pour reprendre le schéma du cours sur l’architecture JEE, JSF correspond aux couches Application et Client. Il est tout à fait possible d’interagir avec des EJBs ou tout autre type de couche métier. Ce sont les JavaBeans qui vont vous permettre d’accéder à une couche Service. Voici un exemple d’architecture JSF + EJB : Dans le cas de petites applications, il est également possible d’interroger directement une base de données depuis les JavaBeans. http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 10 JSF - Framework Web 11 / 108 Voici un exemple d’architecture de site : 2.2. Cycle de vie Nous allons maintenant voir comment un serveur traite une requête. Concrètement JSF est constitué d’une Servlet. Cette Servlet se charge de transformer les balises JSF en un système de composants que l'on peut manipuler côté serveur. De plus, elle produit le code HTML qui correspond aux balises JSF. Lorsqu’un client renvoie une page HTML au serveur, la Servlet va être capable de retrouver l'arbre des composants qui correspond à cette page grâce à des identifiants placés dans le code HTML. La Servlet de JSF peut être exécutée dans un conteneur de Servlet ou dans de Portlet. La classe qui se charge du cycle de vie des pages est « javax.faces.context.FacesContext ». Lorsqu’une requête arrive sur le serveur, une nouvelle instance de FacesContext est créée et elle prend en charge la requête. Voyons plus en détail les phases de traitement des requêtes : http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 11 JSF - Framework Web 12 / 108 2.2.1. Restauration de la vue Lorsqu’une requête arrive sur le serveur, le serveur transmet la requête à la Servlet de JSF. La Servlet va ensuite construire l'arbre des composants représentant la page. Une fois que tous les composants JSF sont instanciés, on passe à la phase suivante. 2.2.2. Application des paramètres de la requête S'il existe des paramètres passés dans la requête, la Servlet va affecter les nouvelles valeurs aux composants concernés. Par exemple, si un utilisateur envoie un formulaire, les valeurs des champs de texte vont être affectés aux composants qui représentent ces champs textes. Certains composants peuvent déclencher un événement dès que leur valeur change. Ces événements sont traités seulement pendant la phase « Invocation de l’application », ou tout de suite si les composants qui déclenche l’événement ont l’attribut « immediate » à true. La gestion des événements pendant le cycle sera revue dans le chapitre sur les événements : « 7 Les comportements ». 2.2.3. Validation des entrées utilisateurs Les composants qui acceptent des entrées utilisateurs peuvent définir des règles de validations. Lors de cette phase, la Servlet vérifie s'il n'y a pas de problèmes de validations. Les événements liés à une validation sont traités à la fin de cette phase. La propriété « immediate » des composants permet de forcer le traitement des validations lors de la phase précédente « Application des paramètres de la requête ». 2.2.4. Mise à jour des valeurs du modèle À partir de maintenant toutes les opérations de mise à jour du modèle ont été faites. Le modèle (JavaBean) qui correspond à chaque composant va être mis à jour pour refléter les nouvelles valeurs. 2.2.5. Invocation de l’application Lors de cette phase, tous les événements sont traités. Cela comprend les événements qui ont été déclenchés pendant la phase « Application des paramètres de la requête ». Enfin la navigation va être résolue, c’est-à-dire que la page de destination va être déterminée. 2.2.6. Rendu de la réponse C'est dans cette dernière phase que la réponse va être rendue sous forme de balises HTML. http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 12 JSF - Framework Web 13 / 108 3. Développer en JSF Quelles sont les étapes nécessaires pour développer une application JSF ? Nous allons voir au travers de cet « Hello World » les étapes minimales nécessaires à la réalisation d’un site en JSF. Dans l’ordre, il faut : • Installer JSF, • Activer JSF dans le fichier « web.xml », • Coder des pages JSF, • Créer des JavaBeans (optionnel), • Configurer l’application grâce au fichier « faces-config.xml » (optionnel). 3.1. Installation de JSF Si vous voulez utiliser JSF 1.2, le plus simple est d’utiliser un serveur conforme à JEE 5. Dans ce cas, le support de JSF est intégré. Vous pouvez par exemple télécharger GlassFish à l’adresse : https://glassfish.dev.java.net/ Si vous voulez utiliser JSF 1.1 ou JSF 1.2 sur un serveur qui ne supporte pas JSF, il faut télécharger une implémentation. Celle de Sun se trouve sur : http://java.sun.com/javaee/javaserverfaces/download.html Dans le répertoire « lib » du fichier zip, il y a tous les « jars » nécessaires au développement d’une application JSF. Ces jars sont nécessaires à la compilation et au déploiement et doivent être placés dans le répertoire « WEB-INF/lib » d’une archive Web (War). Les jars nécessaires à JSF 1.1 sont : • « jsf-api.jar », contient la définition des classes de JSF (package javax.faces), • « jsf-impl.jar », contient l’implémentation des classes de JSF, • « jstl.jar », permet l’utilisation des tags JSTL. Ces tags sont référencés par l’implémentation de JSF, • « standard.jar », permet l’utilisation des tags standards. Ces tags sont référencés par l’implémentation de JSF, • « commons-beanutils.jar », classes d’utilitaires pour manipuler les JavaBeans, • « commons-digester .jar », permet de manipuler du XML, • « commons-collections.jar », extension du framework des Collections, • « commons-logging.jar », permet de créer des logs. Pour JSF 1.2, les seuls jars nécessaires sont: • « jsf-api.jar », contient la définition des classes de JSF (package javax.faces), • « jsf-impl.jar », contient l’implémentation des classes de JSF, http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 13 JSF - Framework Web 3.2. 14 / 108 Activation de JSF C’est dans le fichier « web.xml » que l’on active JSF. Il s’agit simplement de faire une redirection de requête vers la Servlet JSF. Dans notre exemple, nous allons rediriger toutes les requêtes qui commencent par « /faces/ » vers la Servlet de JSF. <?xml version='1.0' encoding='UTF-8'?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <display-name>Nom de l’application</display-name> <!-- Faces Servlet --> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> </servlet> <!-- Mapping --> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>/faces/*</url-pattern> </servlet-mapping> </web-app> Pour JSF 1.2, le début du fichier est : <?xml version='1.0' encoding='UTF-8'?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> Les pages JSF doivent porter l’extension « .jsp ». Il s’agit d’un paramètre de JSF que l’on peut modifier, voir chapitre « 6.3 Paramètres de contexte ». Pour accéder aux pages JSF, il faudra taper une url du style : « /faces/index.jsp », ce qui va permettre de passer par la Servlet JSF. Si on tente d’accéder directement à une page sans le préfixe « /faces/ » dans ce cas la Serlvet JSF n’est pas appelée et il va y avoir des erreurs lors du rendu (balises JSF non interprétées). Voici un autre exemple de mapping, où l’on va accéder aux pages, en tapant le chemin vers la page mais en remplaçant le suffixe « .jsp » par « .jsf ». <web-app> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.jsf</url-pattern> </servlet-mapping> </web-app> http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 14 JSF - Framework Web 15 / 108 Bien qu’on accède aux pages en indiquant l’extension « .jsf » dans le navigateur, les pages que nous allons coder doivent bien porter l’extension « .jsp ». En effet lors de l’appel d’une page, la Servlet JSF cherche une page qui porte le même nom mais avec l’extension « .jsp ». 3.3. Pages JSF Maintenant que JSF est activé nous pouvons écrire une page JSF. Voici une page basique : <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <head> <title>Première page en JSF</title> </head> <body> <f:view> Hello World </f:view> </body> </html> Les 2 premières lignes sont faites pour déclarer les tag-libs utilisées par JSF. La suite est une structure HTML classique. La seule différence est la présence de la balise JSF <f:view>. C’est la balise mère de toutes les balises JSF. Nous voyons dans cet exemple qu’il est possible d’afficher du texte directement comme on le ferait en HTML. Toutefois il existe des balises qui sont prévues spécifiquement pour l’affichage de texte. Celles-ci seront vues dans la suite du cours. 3.4. Ressources nécessaires Une fois les pages JSF terminées, il ne reste plus qu’à packager l’application. Le War à déployer doit avoir la structure suivante : WEB-INF/classes Contient toutes les classes java (les JavaBeans entre autre) WEB-INF/lib Toutes les librairies (jar) nécessaires au projet. WEB-INF/faces-config.xml Fichier de configuration (optionnel) WEB-INF/web.xml *.jsp A la racine, il faut placer toutes les pages jsp Par exemple pour JSF 1.1, ça donne: http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 15 JSF - Framework Web 3.5. 16 / 108 Déploiement Même si votre serveur supporte JSF, vous pouvez intégrer les jars de JSF dans votre archive Web car c’est toujours l’API du serveur qui aura la priorité (vos jars de JSF seront ignorés). Si vous voulez ajouter le support de JSF sur un serveur, vous pouvez copier tous les jars de JSF dans le répertoire de librairies de votre serveur. Par exemple pour Tomcat, il s’agit du répertoire « TOMCAT_HOME/shared/lib ». Ce répertoire dépendant du serveur lui-même, il faut consulter la documentation du serveur. 3.5.1. JBoss JBoss intègre le support de JSF 1.1 depuis sa version 4.0.3. Ce support se base sur l’implémentation d’Apache : MyFaces. Si vous voulez utiliser l’implémentation de Sun, il faut supprimer les jars de MyFaces dans JBoss. Ils se trouvent dans le répertoire : JBOSS_HOME/server/default/deploy/jbossweb-tomcat55.sar/jsf-lib Si « default » est la configuration que vous utilisez. Il faudra ensuite intégrer les jars spécifiques à JSF 1.1 dans votre archive War. Le supporte de JSF 1.2 n’est pas prévu avant la version 5 de JBoss. 3.6. Exemple Nous allons maintenant voir un exemple un peu plus complexe de site Web fait en JSF. Le but est de vous montrer un aperçu de quelques balises et de quelques mécanismes comme la validation d’un formulaire, la gestion de la navigation, l’utilisation de JavaBean. Le site est seulement constitué de 2 pages. La première est un formulaire qui permet à un utilisateur de s’enregistrer. Quand l’utilisateur clique sur bouton pour s’enregistrer, le serveur traite l’enregistrement et le renvoie sur la 2ème page qui affiche un message de confirmation. S’il y a des erreurs de validation du formulaire (champ manquant, mot de passe trop court, ...), des messages sont affichés directement sur le formulaire. http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 16 JSF - Framework Web 17 / 108 Commençons par le code de la page du formulaire, « Inscription.jsp » : <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <head> <title>Inscription</title> </head> <body> <f:view> <h1>Inscription</h1> <h:form> <!-- Le champ texte pour saisir son nom --> <h:inputText id="nom" required="true" size="21" maxlength="20" value="#{user.nom}"> <!-- Le label de ce champ texte--> <h:outputLabel for="nom" value="Nom: "/> <!-- Validateur: le nom doit compter entre 3 et 20 caractères --> <f:validateLength minimum="3" maximum="20"/> </h:inputText> <!-- Affiche les messages d'erreurs concernant le champ d'identifiant 'nom'--> <h:message for="nom" style="color: red"/> <br/> <!-- Le champ texte pour saisir son prénom --> <h:inputText id="prenom" required="true" size="21" maxlength="20" value="#{user.prenom}"> <h:outputLabel for="prenom" value="Prénom: ‡"/> <f:validateLength minimum="3" maximum="20"/> </h:inputText> <h:message for="prenom" style="color: red"/> <br/> <!-- Le champ texte pour saisir son mot de passe --> <h:inputSecret id="motDePasse" required="true" size="21" maxlength="20" value="#{user.motDePasse}"> <h:outputLabel for="motDePasse" value="Password: "/> <f:validateLength minimum="3" maximum="20"/> </h:inputSecret> <h:message for="motDePasse" style="color: red"/> <br/> <h:commandButton value="S'enregistrer" action="success"/> </h:form> </f:view> </body> </html> La syntaxe du type « #{user.nom} » permet d’utiliser un JavaBean. Ici « user » est le nom d’un bean et « nom » est une des propriétés de ce bean. Commençons par écrire la classe de ce bean. Nous verrons ensuite comment lui attribuer un nom. package com.labosun; http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 17 JSF - Framework Web 18 / 108 public class Utilisateur { private String nom; private String prenom; private String motDePasse; public String getNom() { return nom; } public String getMotDePasse() { return motDePasse; } public String getPrenom() { return prenom; } public void setPrenom(String prenom) { this.prenom = prenom; } public void setNom(String nom) { this.nom = nom; } public void setMotDePasse(String motDePasse) { this.motDePasse = motDePasse; } } Maintenant que la classe du bean est écrite, il faut le déclarer dans le fichier de configuration de JSF (faces-config.xml). Cela va permettre de lui attribuer un nom qu’on peut utiliser dans les pages JSF pour y faire référence. Voici la déclaration du bean : <managed-bean> <!-- Nom du bean dans les pages JSF --> <managed-bean-name>user</managed-bean-name> <!-- Classe du bean --> <managed-bean-class>com.labosun.Utilisateur</managed-bean-class> <!-- Portée du bean --> <managed-bean-scope>session</managed-bean-scope> </managed-bean> Maintenant que la première partie du site est faite, il faut écrire la page qui affichera le message de confirmation. Cette page réaffiche uniquement le nom et prénom de la personne qui s’est enregistrée. Confirmation.jsp : <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <head> <title>Inscription</title> </head> <body> <f:view> <h1>Inscription réussie</h1> Bienvenue <h:outputText value="#{user.prenom} #{user.nom}" /> </f:view> http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 18 JSF - Framework Web 19 / 108 </body> </html> Comme vous pouvez le constater, on réutilise ici la syntaxe « #{user.prenom} » pour faire référence au bean. Enfin, la dernière étape est de définir une règle de navigation. Cette règle permet de définir la page de destination qui sera associée au bouton d’enregistrement dans la page « Confirmation.jsp ». Pour rappel le code du bouton est le suivant: <h:commandButton value="S'enregistrer" action="success"/> L’attribut « action » définit une chaîne qui fait référence à la page de destination. <navigation-rule> <!-- Page pour laquelle on définit la règle --> <from-view-id>/Inscription.jsp</from-view-id> <navigation-case> <!--Chaîne qui est utilisée pour référencer la page de destination --> <from-outcome>success</from-outcome> <!-- Page de destination --> <to-view-id>/Confirmation.jsp</to-view-id> </navigation-case> </navigation-rule> Voyons maintenant le résultat. Voici la page d’inscription : La mise en forme n’est pas très bonne, mais nous verrons dans la suite du cours commet y remédier. Lorsqu’on s’enregistre, on arrive sur la page de confirmation : Voici les messages d’erreurs qui sont affichés lorsque la validation d’un des champs échoue : On remarque ici que les messages d’erreur sont en français, ils sont personnalisés en fonction de la langue du client. Par contre notre formulaire lui est uniquement en français. Le chapitre « 8.1 Internationalisation » explique comment gérer un site multi langues. http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 19 JSF - Framework Web 20 / 108 De plus, les messages d’erreurs ne sont pas très clairs, le chapitre « 8.2.1 Refaire les messages » explique comment personnaliser les messages d’erreurs. http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 20 JSF - Framework Web 21 / 108 4. Les briques de base La classe mère des composants est « javax.faces.component.UIComponent ». Cette classe définit les attributs que possèdent tous les composants JSF. Il s'agit de composants non spécifiques au HTML, mais qui fournissent le même type de fonctionnalités: liens, tableau, formulaire, boutons, menu déroulant, liste, ... Les composants spécifiques à HTML héritent tous d'un composant standard. Ils se trouvent dans le package « javax.faces.component.html ». Dans la pratique on utilise une balise JSF pour utiliser un composant, et les attributs de cette balise pour appeler les méthodes ou propriétés du composant. Par exemple, la classe UIComponent définit la propriété « id ». Cette propriété est accessible directement depuis une balise JSF grâce à l’attribut du même nom : « id ». ce qui donne : public class UIComponent { private int id; public int getId() {...} public void setId() {...} } Et pour n’importe quelle balise JSF on pourra écrire : <h:baliseJSF id="identifiant" /> Attention: • Certaines propriétés ne sont pas directement manipulables depuis les balises JSF. • Il existe quelques attributs de balises qui ne portent pas exactement le même nom que la propriété du JavaBean. http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 21 JSF - Framework Web 4.1. 22 / 108 Les comportements Il existe 5 interfaces qui permettent de définir un comportement pour un composant. Interface Description StateHolder Indique que le composant possède un état qui doit être sauvegardé entre les requêtes. Cette interface est implémentée par UIComponent. Ainsi tous les composants JSF possèdent un état que le serveur sauvegarde entre les requêtes utilisateurs. NamingContainer Indique que tous les composants fils de ce composant doivent avoir un identifiant unique. Il n’est pas obligatoire de donner un identifiant aux composants. Si vous n’en précisez pas, un identifiant est généré automatiquement par le serveur. ValueHolder Le composant possède une valeur. Cette valeur peut être interne au composant ou liée à un JavaBean. La valeur peut être convertie à l’aide d’un convertisseur. Voir chapitre « 7.3 Conversions de données ». EditableValueHolder Hérite de ValueHolder. Cette interface définit que la valeur du composant peut être modifié et qu’un événement est généré dès que la valeur change. De plus il est possible de mettre en place un système de validation pour ces composants. ActionSource Indique qu’un composant peut générer un événement suite à une activation de l’utilisateur. Exemple : un lien HTML génère une action lorsqu’on clique dessus. Note : En JSF 1.2, l’interface « ActionSource » a été étendue en « ActionSource2 ». Les balises JSF des composants qui implémentent ValueHolder possèdent l’attribut « converter ». Cet attribut permet de convertir la valeur du composant (qu’elle soit interne ou liée à un JavaBean). Cela sert par exemple à convertir une chaîne de type « 10/05/06 » en un objet Date. Les balises JSF des composants qui implémentent EditableValueHolder possèdent les attributs : • « validator », cela permet de définir une méthode ou un composant qui va se charger de vérifier les entrées utilisateurs. • « valueChangeListener », dès que la valeur du composant change, un événement est déclenché Les balises JSF des composants qui implémentent ActionSource possèdent l’attribut « actionListener ». Cet attribut permet de définir l’écouteur à appeler (côté serveur) lorsque le composant est activé par l’utilisateur. L'utilisation de ces comportements est détaillée dans le chapitre « 7. Les comportements ». http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 22 23 / 108 JSF - Framework Web 4.2. Les composants génériques JSF est prévu pour être indépendant du HTML. C’est pourquoi on va retrouver un système de rendu qui permet de définir comment doit être affiché un composant. Dans la pratique, il n’existe que l’implémentation pour le HTML. On va ainsi retrouver 2 hiérarchies de composants, la première pour les composants génériques de JSF, la deuxième pour les composants HTML. Voici l’ensemble des composants package « javax.faces.component ». génériques de JSF, Classe (ActionSource) UIData (NamingContainer) UIForm se trouvent tous dans le Description (interfaces implémentées) UICommand ils Classe mère de tous les composants qui peuvent générer un événement lorsqu’ils sont activés Permet de traiter un ensemble de données provenant d’une Collection ou d’un tableau. (NamingContainer) Représente un formulaire. C’est-à-dire un ensemble de composant qui vont pouvoir être modifiés par l’utilisateur. UIGraphic Prend en charge l’affichage d’images. UIInput (extends UIOutput) Permet de saisir des données. (EditableValueHolder) UIMessage et UIMessages Permet d’afficher un message provenant du système de message interne à JSF. Ce point sera vu dans le chapitre « 8.2 La gestion des messages ». UIOutput Permet l’affichage de texte. (ValueHolder) UIPanel Regroupe plusieurs composants JSF pour les manipuler comme un élément unique. UISelectBoolean (extends UIInput) Permet de définir une valeur booléenne (EditableValueHolder) UISelectMany (extends UIInput) Permet de sélectionner plusieurs éléments parmi une liste (EditableValueHolder) UISelectOne (extends UIInput) Permet de sélectionner un élément parmi une liste (EditableValueHolder) Tous ces composants supportent les attributs suivants. http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 23 JSF - Framework Web 24 / 108 4.2.1. L’attribut id L’attribut « id » permet de donner un nom unique à un composant. L'utilisation d'identifiant n'est pas obligatoire, elle sert uniquement à référencer des composants dans la page. Un identifiant doit se conformer aux règles suivantes : • L’identifiant doit commencer par une lettre (voir la méthode Character.isLetter()) ou un underscore '_'. • Les caractères suivants peuvent être des lettres (voir la méthode Character.isLetter()), des chiffres (voir la méthode Character.isDigit()), le tiret '-' ou un underscore '_'. Exemple : <h:baliseJSF id="identifiant" /> Note : Pour optimiser les performances de JSF il est recommandé d’utiliser des identifiants le plus court possible. 4.2.2. L’attribut renderer Cet attribut vous permet de choisir si un composant sera affiché ou non. Pour ne pas afficher un composant il suffit de mettre « renderer » à false. Exemple : <h:baliseJSF renderer="false" /> Cet attribut peut servir à afficher un message seulement si une recherche n’a donné aucun résultat. 4.2.3. L’attribut immediate Comme vu dans le chapitre « 2.2 Cycle de vie », cet attribut permet de déterminer dans quelle phase du cycle de vie d'une requête les événements sont traités. 4.3. Les composants HTML Les composants HTML se basent sur les composants génériques pour fonctionner. Ils se trouvent tous dans le package « javax.faces.component.html ». Plutôt que de lister tous les composants HTML ici, nous les verrons dans le chapitre « 5 JSF Tag-lib » où vous pourrez directement voir comment les utiliser dans une page JSF à l’aide de balises. En plus des attributs standard, les composants génériques proposent les attributs suivants : 4.3.1. Style CSS L’attribut « style » permet de définir le code CSS d’une balise. Voici un exemple qui utilise la balise <h:outputText>, cette balise sera vue plus tard dans le cours. <h:outputText style="font-size : 150%; color: red" value="Hello"/> Ceci sera traduit par le code HTML : http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 24 JSF - Framework Web 25 / 108 <span style="font-size : 150%; color: red">Hello</span> 4.3.2. Classe CSS L’attribut « styleClass » permet de définir la classe CSS d’une balise. Exemple : <h:baliseJSF styleClass=”titre1” /> 4.3.3. Escape Certains composants possèdent l'attribut « escape ». S'il est mis à true, les caractères '<', '>' et '&' sont convertis en HTML. 4.3.4. Evénements JavaScript Il est possible de définir les attributs javascript pour les balises concernées. On retrouve ainsi: « onclick, ondblclick, onblur, onchange, onclick, ondblclick, onfocus, onkeydown, onkeypress, onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup, onreset, onselect, et onsubmit ». Il faut consulter la documentation HTML pour savoir quels composants possèdent tel attribut. 4.4. Liaison composants et JavaBeans Les JavaBeans représente la partie modèle d’après le modèle MVC. Ils permettent de bien séparer les différents concepts de l’application. Un composant peut être lié à un JavaBean de 2 manières différentes : • uniquement la valeur du composant est liée avec une propriété du JavaBean (uniquement pour les composants qui possèdent une valeur). • l’instance du composant elle-même est liée à une propriété du JavaBean. Toutes les expressions qui utilisent des JavaBean depuis une page JSF sont écrites en EL (Expression Language). Il s’agit d’un langage spécifique aux pages Web en Java. A partir de JSF 1.2 ce langage a été unifié avec le langage spécifique à JSP sous le nom Unified Expression Language. Nous nous contenterons ici d’utiliser la structure la plus simple de ce langage. Le prochain chapitre « 4.5 Unified Expression language » l’aborde plus en détail. Une expression en EL commence par « #{ » et se termine par « } ». Dans une expression, on utilise une notation objet. On peut utiliser n’importe quelle variable en l’appelant par son nom. A partir de cette variable on peut appeler n’importe quelle méthode ou propriété. Ce qui donne : #{javaBean.method} #{javaBean.property} http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 25 JSF - Framework Web 26 / 108 4.4.1. Valeur Pour lier la valeur d’un composant il faut utiliser l’attribut « value » et spécifier une référence vers une propriété de JavaBean en EL. Ce qui donne : <h:baliseJSF value=”#{javaBean.property}” /> Il y a des restrictions sur les types de valeur que l'on peut associer à un composant JSF. Composant Valeurs possibles UIInput, UIOutput, UISelectItem, UISelectOne Une primitive, une String, un Number ou n'importe quelle classe pour laquelle il existe un convertisseur de données. Voir le chapitre « 7.3 Conversions » pour plus d'informations sur les conversions. UIData Tableau de JavaBean, liste de JavaBean, JavaBean simple, java.sql.ResultSet, javax.servlet.jsp.jstl.sql.Result ou javax.sql.RowSet. UISelectBoolean boolean ou Boolean UISelectItems String, Collection, Tableau ou Map UISelectMany Tableau ou List contenant n'importe quel type. Les restrictions seront reprécisées lorsque nous verrons les balises JSF. 4.4.2. Instance Dans le cas où l’on voudrait lier l’instance d’un composant avec une propriété d’un bean, la propriété doit être du même type que le composant JSF. On utilise l’attribut « binding » pour référencer la propriété du JavaBean. Par exemple, je veux lier le composant HtmlOutputText à un JavaBean. Je vais déclarer mon bean : public class LinkedBean { private HtmlOutputText composant; public HtmlOutputText getComposant() { return this.composant; } public void setComposant(HtmlOutputText c) { this.composant = c; } } Et je peux ensuite lier mon JavaBean de la manière suivante : <h:outputText binding=”#{linkedBean.composant}” /> http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 26 JSF - Framework Web 27 / 108 La différence entre l’attribut « value » et l’attribut « binding » est que dans le premier cas on lie la valeur . Par exemple un champ texte accepte un « String », un « Number » ou un type primitif. L’attribut « value » peut donc être de type « String », « Number » ou primitf. Le champ texte est représenté par la classe « HtmlInputText ». La hiérarchie de cette classe est la suivante : java.lang.Object javax.faces.component.UIComponent javax.faces.component.UIComponentBase javax.faces.component.UIOutput javax.faces.component.UIInput javax.faces.component.html.HtmlInputText L’attribut binding peut donc avoir pour type, n’importe quelle classe de cette hiérarchie. 4.5. Unified Expression language L'Unified EL (Unified Expression Language) est un langage créé pour uniformiser la syntaxe utilisée dans les pages Web lorsque l'on veut accéder à un objet Java. Cette syntaxe particulière a fait son apparition dans JSP 2.0 et dans JSF 1.0 sous le nom Expression Language. Les JSP et JSF utilisaient alors 2 versions différentes de l’EL. Ce n’est qu’avec JSP 2.1 et JSF 1.2 que le langage a été unifié sous le nom « Unified Expression Language ». Par défaut, les Servlet version 2.3 et inférieures ignorent cette syntaxe. Il est possible de changer ce comportement grâce à la directive: <%@ page isELIgnored ="true|false" %> En JSP, une expression unified EL est sous la forme « ${ } ». En JSF, on utilise la syntaxe « #{} ». Sans rentrer dans les détails qui dépassent le cadre de ce cours, sachez simplement que le mélange de code JSP avec « ${} » et de code JSF avec « #{} » pose problème. Le plus sûr dans vos applications est de n’utiliser que du code JSF et remplacer tout ce qui est balises JSP par leur équivalent en balises Facelets (voir chapitre « 9 Facelets »). 4.5.1. Variables Il est possible de référencer n'importe quel objet accessible dans la page avec son nom de variable. Par exemple si on écrit: #{person} Le conteneur Web va chercher un objet qui porte le nom « person » dans la page, la requête, la session ou l'application. Pour accéder à une variable ou une méthode d'un objet, il faut utiliser l'opérateur « . ». Voici comment accéder à la propriété « name » de la variable « person ». #{person.name} http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 27 JSF - Framework Web 28 / 108 Voici comment accéder à la méthode « checkLogin » de la variable « person » : #{person.checkLogin} Pour accéder aux valeurs d’un tableau ou d’une liste il faut utiliser l’opérateur « [] ». Exemple, on veut obtenir l’élément d’indice 2 dans un tableau : #{uelBean.tab[2]} Dans le cas d'une liste, l'expression « #{bean[property]} » est équivalente à « bean.get(property) ». Si l'exception IndexOutOfBoundsException est levée, null est renvoyé. Pour toutes les autres exceptions, une erreur est renvoyée. Exemple : On souhaite accéder au premier élément d’une liste : #{uelBean.liste[0]} Dans le cas d'une Map, l'expression « #{bean['clef']} » est équivalente à « bean.get(clef) » ou null si la map ne contient pas la clef. Attention de bien entourer le nom de la clef par des apostrophes. Exemple : On veut obtenir la valeur correspond à la clef « login » : #{uelBean.map['login']} L’opérateur « [] » peut également servir à appeler une propriété ou une méthode. Dans ce cas, il faut entourer le nom de la propriété ou méthode par des apostrophes (comme pour l’accès aux éléments d’une Map). Exemples : Accès à la propriété « name » de la variable « person ». #{person['name']} Ceci est équivalent à : #{person.name} Appel de la méthode « checkLogin » de la variable « person ». #{person['checkLogin']} Ceci est équivalent à : #{person.checkLogin} 4.5.2. Objets implicites Il existe des objets implicites dans les pages JSF. Ils sont toujours accessibles et peuvent être récupérés grâce à une expression EL. http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 28 JSF - Framework Web 29 / 108 Nom de l'objet Description applicationScope Une Map qui associe le nom des attributs d'application à leur valeur. cookie Une Map qui associe le nom des cookies à leur valeur. facesContext L'instance FacesContext courante header Une Map qui contient les en-têtes HTTP associées à leur valeur. headerValues Une Map qui associe chaque nom d'en-tête HTTP à un tableau de String. Le tableau contient toutes les valeurs d'un en-tête. initParam Une Map qui contient les paramètres d'initialisation de l'application Web. param Une Map qui associe le nom des paramètres de la requête à leur valeur. paramValues Une Map qui associe chaque nom de paramètre de la requête à un tableau de String. Le tableau contient toutes les valeurs d'un paramètre. requestScope Une Map qui associe les attributs de la requête à leur valeur. sessionScope Une Map qui associe le nom des attributs de sessions à leur valeur. view Le composant racine de l'arbre de composant actuel. 4.5.3. Opérateurs L’unified EL permet d’utiliser la majorité des opérateurs de Java. La nouveauté vient du fait que certains opérateurs ont maintenant leur équivalent en mot-clef. • Arithmétiques : +, -, *, /, %. Avec les deux mot-clefs : « div » et « mod ». • Logiques : &&, ||, !. Avec les mot-clefs : « and », « or » et « not ». • Relationnels : ==, « eq », !=, « ne », <, « lt », >, « gt », <=, « le », >=, « ge ». Ces opérateurs permettent de comparer des types primitifs et des String. • Ternaire : expression ? val1 : val 2. • instanceof • « empty » permet de tester si une valeur est null ou si un tableau, liste est vide. http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 29 JSF - Framework Web 30 / 108 5. JSF Tag-lib Voyons maintenant l'ensemble des balises utilisables dans une page JSF. Toutes les balises qui manipulent un composant JSF (non HTML) sont préfixées par « f » par défaut. Le nom de la balise est celui du composant sans le préfixe « UI ». Toutes les balises qui manipulent un composant HTML sont préfixées par « h » par défaut. Le nom de la balise est celui du composant sans le préfixe « Html ». 5.1. Vue JSF La première balise à utiliser pour faire du JSF est <f:view>, cette balise permet de déclarer la vue qui va contenir toutes les autres balises. <f:view> <!-- Balises JSF --> </f:view> 5.2. Texte simple La balise <f:verbatim> génère un composant UIOutput à partir de son contenu. C’est la plus simple pour afficher du texte, mais il ne s’agit pas d’une balise spécifique à HTML. Il n’est donc pas possible d’utiliser tous les attributs propres à HTML comme le style CSS. Exemple d’utilisation : <p> <f:verbatim>Hello Verbatim</f:verbatim> </p> Ce qui produit le code HTML: <p>Hello Verbatim</p> 5.3. Les composants de sorties Les balises permettant d’afficher du texte sont : • <h:outputText> • <h:outputFormat> • <h:outputLabel> Elles correspondent aux composants : • HtmlOutputText. • HtmlOutputFormat, • HtmlOutputLabel, Ils héritent tous de UIOuput qui implémente ValueHolder. L’attribut « value » permet de définir le texte à afficher. Cet attribut peut être associé à une primitive, une String, un Number ou n’importe quelle classe pour laquelle il existe un convertisseur de données (voir le chapitre « 7.3 Conversions » pour plus d'informations sur les conversions). http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 30 JSF - Framework Web 31 / 108 5.3.1. Texte simple La balise la plus simple pour afficher une seule ligne de texte est : <p> <h:outputText value="texte"/> </p> Ce qui sera traduit par le code HTML : <p>texte</p> Voici un autre exemple, mais cette fois-ci, nous allons inclure du texte à l’intérieur de la balise. <p> <h:outputText value="Another test of " style="color: red"> outputText </h:outputText> </p> Ce qui produit le code HTML : <p>outputText <span style="color: red">Another test of </span></p> Et en graphique, cela donne : outputText Another test of On remarque ici, que le texte à l’intérieur de la balise est affiché avant le texte de l’attribut « value », de plus seulement ce texte utilise le style CSS. Note : Il est possible d’afficher un texte provenant d’un fichier de ressource. Ceci sera vu lors du chapitre « 8.1 Internationalisation ». 5.3.2. Message formaté La balise <h:outputFormat> affiche un message formaté grâce à la méthode format() de la classe MessageFormat. Il est possible de passer des paramètres en imbriquant des balises <f:param/>. Exemple de code JSF : <p> <h:outputFormat value="outputFormat is {0}"><br /> <f:param value="great" /> <!-- Passage d'un paramètre --> </h:outputFormat> </p> Ce qui donne en HTML : <p>outputFormat is great</p> Autre utilisation : <h:outputFormat value="Arrondi à 2 chiffres: {0,number,#.##}, à 1 chiffre: {0,number,#.#}"> <f:param value="#{basic.nb}" /> </h:outputFormat> Ce qui donne : Arrondi à 2 chiffres: 4,15, à 1 chiffre: 4,2 Pour plus de détails sur les patterns utilisables, il faut consulter la documentation de la classe MessageFormat. http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 31 JSF - Framework Web 32 / 108 5.3.3. Label La balise <h:outputLabel> représente la balise <label> en HTML. Elle permet d’affecter un label à un composant d’entrée utilisateur (tel qu’un champ texte, une checkbox, …). Cette balise s’utilise en tant que balise fille de l’élément auquel elle fait référence. L’attribut « for » contient l’identifiant pour lequel on utilise ce label. Exemple : <h:inputText id="address" value="your address here"> <!-- champ texte --> <h:outputLabel for="address" value="Address:" /> </h:inputText> Ce qui va générer le code HTML suivant : <label for="address">Address:</label> <input id="address" type="text" name="address" value="your address here" /> Il est également possible d’utiliser la balise <h:outputLabel> toute seule. Exemple : <h:outputLabel for="city" value="City:" /> <h:inputText id="city" value="Strasbourg" /> Voici au final ce qu’on obtient en combinant les 2 exemples précédents : 5.4. Formulaire Cette balise représente un formulaire. Le composant implémente « NamingContainer », ce qui veut dire que toutes les balises à l’intérieur d’un formulaire doivent posséder un identifiant unique. Exemple : <h:form> </h:form> JSF va ajouter quelques informations lors de la traduction de cette balise en HTML. Le type d’envoi du formulaire est toujours en « POST » et l’encodage en « application/x-www-form-urlencoded ». <form id="_id0" method="post" action="/HW/04-Form.jsf" enctype="application/x-www-form-urlencoded"> <input type="hidden" name="_id0" value="_id0" /> </form> Voici un dernier exemple qui illustre l’ajout d’identifiant pour toutes les balises filles d’un formulaire : <h:form> <h:outputText id="name" value="texte" /> </h:form> http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 32 JSF - Framework Web 33 / 108 Et en HTML : <form id="_id1" method="post" action="/HW/04-Form.jsf" enctype="application/x-www-form-urlencoded"> <span id="_id1:name">texte</span> <input type="hidden" name="_id1" value="_id1" /> </form> Tous les composants qui n’ont pas d’identifiant s’en voient attribuer un. Les identifiants utilisés sont préfixés par l’identifiant du formulaire ce qui permet d’utiliser plusieurs fois le même identifiant dans des formulaires différents. 5.5. Les composants d’entrée Les composants pour permettre de saisir des données sont : • HtmlInputText, • HtmlInputTextarea, • HtmlInputSecret, • HtmlInputHidden. Ils héritent tous de UIInput qui implémente EditableValueHolder. L’attribut « value » de ces balises permet d’affecter une valeur par défaut à un champ. La valeur peut être liée à une primitive, une String, un Number ou n'importe quelle classe pour laquelle il existe un convertisseur de données. Il est possible d’utiliser l’attribut « required » pour indiquer qu’un champ est obligatoire. 5.5.1. Champ texte La balise la plus simple pour permettre la saisie de texte est <h:inputText>. <h:inputText maxlength="20" size="20" value="default value"/> Ce qui va générer le code HTML : <input type="text" value="default value" maxlength="20" size="20" /> 5.5.2. Champ texte sur plusieurs lignes La balise <h:inputTextArea> permet de saisir un texte sur plusieurs lignes <h:inputTextarea cols="10" rows="10" /> En HTML, ça donne : <textarea cols="10" rows="10"></textarea> 5.5.3. Champ de mot de passe La balise <h:inputSecret> permet de saisir un mot de passe. L’attribut « redisplay » permet que le mot de passe soit conservé lorsque la page est rechargée (cet attribut est à false par défaut). <h:inputSecret redisplay="false" value="#{bean.password}" /> http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 33 JSF - Framework Web 34 / 108 Ce qui donne le code HTML: <input type="password" /> 5.5.4. Champ caché La balise <h:inputHidden> permet d’inclure un champ caché dans une page. <h:inputHidden value="#{bean.cache}"/> Ce qui donne le code HTML: <input type="hidden" value="valeur" /> 5.6. Lien Il existe 2 types de liens en JSF: • les liens simples, qui ne déclenchent aucun événement côté serveur. • les liens qui vont déclencher un événement côté serveur et qui vont permettre de gérer la navigation du site à partir d'un seul fichier. Tous les liens internes à votre site (qui renvoie sur votre site) doivent être du 2ème type. En effet grâce à ces liens plus évolués, vous allez pouvoir définir la navigation de votre site dans le fichier de configuration de JSF. 5.6.1. Lien simple La balise <h:outputLink> permet d’afficher un lien. Elle utilise le composant « HtmlOutputLink » qui hérite de UIOutput qui implémente ValueHolder. L’attribut « value » permet de définir l’url du lien. La valeur peut être associée à une primitive, une String, un Number ou n’importe quelle classe pour laquelle il existe un convertisseur de données. Il n’existe pas d’attribut pour définir le texte du lien. A la place, il suffit d’imbriquer une balise d’affichage tel que <h:outputText>. Exemple : <h:outputLink value="url"> <h:outputText value="Texte du lien" /> </h:outputLink> Il est possible d’imbriquer plusieurs balises pour définir le texte: <h:outputLink value="url"> <h:outputText value="Texte " /> <h:outputText value="du " /> <h:outputText value="lien" /> </h:outputLink> Ce qui dans les 2 cas sera traduit par le code HTML : <a href="url">Texte du lien</a> http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 34 JSF - Framework Web 35 / 108 Toutes les urls sont relatives à l’url courante. Il est possible de se référer à la racine du site en préfixant l’url par « / ». Si on a l’url suivante : http://localhost:8080/JSFCours/faces/testLiens.jsp Où « JSFCours » est le contexte du site et « faces » le mapping vers la servlet JSF. Alors : • « index.jsp » pointe vers « http://localhost:8080/JSFCours/faces/index.jsp » • « /index.jsp » pointe vers « http://localhost:8080/index.jsp » Il est possible de récupérer le contexte du site et le mapping vers la servlet JSF grâce aux expressions suivantes : • #{facesContext.externalContext.requestContextPath}, contexte du site • #{facesContext.externalContext.requestServletPath}, mapping vers la Servlet de JSF. Cette chaîne est vide si le mapping est en mode suffixe. • #{facesContext.externalContext.requestPathInfo}, le chemin vers la page actuelle en partant de l’url de la Servlet de JSF. Par exemple si on a l’url « http://localhost:8080/JSFCours/faces/testLiens.jsp » alors on récupèrera les valeurs suivantes : • facesContext.externalContext.requestContextPath = /JSFCours • facesContext.externalContext.requestServletPath = /faces • facesContext.externalContext.requestPathInfo = /testLiens.jsp 5.6.2. Lien JSF Le composant pour créer un lien est « HtmlCommandLink ». Il hérite de UICommand qui implémente « ActionSource ». Ce type de lien n’utilise pas d’URL pour indiquer la page sur laquelle aller mais une chaîne. Cette chaîne doit être référencée dans le fichier de navigation de JSF. C’est grâce à ce fichier que JSF sait à quelle page de destination correspond telle chaîne d’action. Voir chapitre « 6.2 Navigation ».pour plus de détails sur les chaînes d’actions. Pour créer un lien on utilise la balise <h:commandLink>. Cette balise doit se trouver à l’intérieur d’une balise <h:form> pour fonctionner. L'attribut « value » permet de définir le texte du lien (mais pas l’url de destination). La valeur peut être liée à une primitive, une String, un Number ou n’importe quelle classe pour laquelle il existe un convertisseur de données. La chaîne d’action se précise grâce à l’attribut « action ». Si on n’en précise aucune, le lien retourne sur la page actuelle. Exemple : <h:form> <h:commandLink action="success" value="lien JSF" /> </h:form> http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 35 36 / 108 JSF - Framework Web Tout comme pour la balise <h:outputLink> il est possible d’imbriquer des balises qui vont définir les éléments qui peuvent servir de lien. L’équivalent de l’exemple précédent donne alors : <h:form> <h:commandLink action="success"> <h:outputText value="lien JSF" /> </h:commandLink> </h:form> Ce qui dans les 2 cas produit un code HTML similaire à : <form method="post" action="/HW/06-Liens.jsf" enctype="application/x-www-form-urlencoded"> <a href="#">lien success</a> </form> Note : Le code généré a été simplifié pour ne montrer que l’essentiel. 5.6.3. Paramètres Il est possible de passer des paramètres à un lien en imbriquant des balises <f:param>. Cette balise possède l’attribut « name » qui est le nom du paramètre et l’attribut « value » qui est la valeur du paramètre. <h:form> <h:outputLink value="index.jsp"> <h:outputText value="index.jsp"/> <f:param name="id" value="2"/> <f:param name="sortBy" value="price"/> </h:outputLink> <br/> <h:commandLink> <f:param name="id" value="6"/> <f:param name="sortBy" value="name"/> </h:commandLink> </h:form> La valeur des paramètres peut ensuite être « #{param.NomParamètre} ». Ce qui dans notre cas donne : récupérée grâce à l’expression <h:outputText value="id=#{param.id}, sortBy=#{param.sortBy}" /> La présence d’un paramètre peut être testé grâce à l’expression : « #{!empty param.NomParamètre} ». Ce qui peut par exemple servir à afficher des balises seulement si un paramètre existe. Exemple : <h:outputText value="(Ne s'affiche que si le paramètre 'id' existe) id=#{param.id}" rendered="#{!empty param.id}"/> http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 36 JSF - Framework Web 5.7. 37 / 108 Bouton Le composant pour créer un bouton est « HtmlCommandButton ». Il hérite de UICommand qui implémente ActionSource (et ActionSource2 pour JSF 1.2). Un bouton fonctionne comme le lien de la classe HtmlCommandLink. Exemple : <h:form> <h:commandButton action="chaîne" value="Envoyer" /> </h:form> Ce qui donne le code HTML: <input type="submit" value="Envoyer" /> 5.8. Checkbox Il existe 2 composants pour afficher des checkbox: • <h:selectBooleanCheckbox> pour afficher une seule checkbox, • <h:selectManyCheckbox> pour afficher une série de checkbox. 5.8.1. Checkbox simple L’attribut « value » détermine si la case est cochée. La valeur peut être un boolean ou Boolean. Exemple : <h:selectBooleanCheckbox id="choix" value="#{bean.property}"/> En HTML : <input id="choix" type="checkbox" /> Pour afficher un texte à côté de la checkbox, il faut imbriquer une balise qui affiche du texte comme la balise <h:outputLabel> par exemple. Exemple : <h:selectBooleanCheckbox id="choix" value="#{bean.property}"> <h:outputLabel for="choix" value="le texte de la checkbox" /> </h:selectBooleanCheckbox> 5.8.2. Checkbox multiples Pour afficher plusieurs checkbox il faut utiliser la balise <h:selectManyCheckbox>. Elle possède l'attribut « layout » qui permet d'afficher les cases horizontalement ou verticalement. Les valeurs sont: • • « lineDirection » pour que les cases s’affichent sur la même ligne, « pageDirection » pour afficher une case par ligne. La liste des cases sélectionnées par l'utilisateur peut être stockée dans un objet de type List ou Array. Cette liste est référencée par l'attribut « value ». Elle définit également les cases qui sont cochées par http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 37 JSF - Framework Web 38 / 108 défaut. Nous verrons plus tard comment définir cette liste de cases sélectionnées, nous allons d’abord voir comment définir le label et la valeur de chaque case. Pour définir l'ensemble des cases qui seront affichées vous pouvez: • imbriquer autant de balises <f:selectItem> qu'il y a de choix, • imbriquer une seule balise <f:selectItems>. Cette balise va référencer une liste de SelectItem. 5.8.2.1. SelectItem La balise imbriquée <f:selectItem> représente une case à cocher. Ses attributs « itemValue » et « itemLabel » représentent respectivement la valeur et le label d'une case. La valeur de chaque case peut être liée à une primitive, une String, un Number ou n’importe quelle classe pour laquelle il existe un convertisseur de données. Voir chapitre « 7.3 Conversions de données » pour plus de détails. Exemple: <h:selectManyCheckbox id="ville" layout="pageDirection" value="#{bean.selectedVilles"> <f:selectItem itemValue="StrasValue" itemLabel="Strasbourg" /> <f:selectItem itemValue="LyonValue" itemLabel="Lyon" /> <f:selectItem itemValue="ParisValue" itemLabel="Paris" /> </h:selectManyCheckbox> Ce qui donne : Voici la classe du bean: public class Bean { private List selectedVilles = new ArrayList(); public List getSelectedVilles() { return selectedVilles; } public void setSelectedVilles(List selectedVilles) { this.selectedVilles = selectedVilles; } } Ici, la valeur des items est de type « java.lang.String », « #{bean.selectedVilles} » contient les villes sélectionnées. C’est-à-dire les chaînes qui sont définies par l’attribut « itemValue ». Par exemple si l’utilisateur coche la case « Strasbourg » alors « selectedVilles » va contenir uniquement « StrasValue ». Si l’utilisateur coche les 2 cases, « selectedVilles » va contenir « StrasValue » et « LyonValue ». Il faut bien faire attention que la valeur des cases puisse être incluse dans l’objet List ou Array qui contient les cases sélectionnées. C’est-à-dire que dans notre cas on peut utiliser un tableau de String (String[]) pour contenir les villes sélectionnées. Mais on ne peut pas utiliser un tableau d’entier (int[]) car les valeurs des cases sont de type String. Encore une fois, si vous voulez que la valeur d’une case soit d’un type que vous avez créé vous-même, il faut utiliser un convertisseur. En effet, JSF doit pouvoir convertir la valeur référencé par http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 38 JSF - Framework Web 39 / 108 « itemValue » en un objet de type String (lors de la traduction en HTML et recréer l’objet à partir de cette même String lors de la reconstruction de l’arborescence d’objet lors de la réception d’une requête). 5.8.2.2. SelectItems Si vous ne voulez pas initialiser la liste des cases dans les balises JSF, il suffit de créer une liste, une Map ou un tableau de SelectItem dans un JavaBean (ou à partir du fichier de configuration de JSF). Le constructeur de cette classe prend en paramètre la valeur et le nom de l'item. Prenons comme exemple la création de la liste dans un JavaBean. Le chapitre « 6.1.3 Exemple : Déclaration d'une liste de SelectItem » décrit la création dans le fichier de configuration. Le JavaBean précédent devient: public class Bean { private List selectedVilles = new ArrayList(); private List allVilles = new ArrayList(); public Bean() { //On construit la liste des villes disponibles allVilles.add(new SelectItem("StrasbourgValue", "Strasbourg :")); allVilles.add(new SelectItem("LyonValue", "Lyon :")); //éléments sélectionnés par défaut selectedVilles.add("LyonValue"); } public List getAllVilles() { return allVilles; } public void setAllVilles(List allVilles) { this.allVilles = allVilles; } public List getSelectedVilles() { return selectedVilles; } public void setSelectedVilles(List selectedVilles) { this.selectedVilles = selectedVilles; } } Il ne reste plus qu'à référencer la liste de SelectItem à l'aide de la balise <f:selectItems>, ce qui donne: <h:selectManyCheckbox id="ville" value="#{bean.selectedVilles}"> <f:selectItems value="#{bean.allVilles}" /> </h:selectManyCheckbox> Comme dans le cas de la balise <f:SelectItem>, la valeur des cases doit être de type primitif ou Number. Si ce n’est pas le cas, il faut fournir un convertisseur. Si vous voulez utiliser un tableau pour stocker les villes, il faut le déclarer de la manière suivante : SelectItem[] allVilles ; http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 39 JSF - Framework Web 40 / 108 Si vous voulez utiliser un objet de type Map, dans ce cas les clefs de la map seront les labels des cases à cocher et les valeurs de la Map correspondront à la valeur des cases à cocher. Exemple : Map allVillesMap = new HashMap(); allVillesMap.put("Strasbourg :", "StrasbourgValue "); allVillesMap.put("Lyon :", "LyonValue "); 5.8.2.3. Utilisation de groupes Il est possible de créer des groupes de SelectItem, pour ça il faut forcément utiliser la balise <f:selectItems>. La création des groupes va se faire grâce à la classe SelectItemGroup. Le constructeur de cette classe prend en paramètre : • Le nom du groupe, • Sa description, • Son activation (false pour actif et true pour inactif), • Un tableau de SelectItem qui compose ce groupe Une fois un SelectItemGroup instancié, il suffit de l’ajouter à la liste qui contient tous les SelectItem. Il est possible de mélanger des SelectItem et des SelectItemGroup. Exemple : //Liste des villes List allGroupVilles = new ArrayList(); //France SelectItem[] villesFrance = new SelectItem[3]; villesFrance[0] = new SelectItem("StrasValue", "Strasbourg”); villesFrance[1] = new SelectItem("LyonValue", "Lyon :"); villesFrance[2] = new SelectItem("ParisValue", "Paris :"); //Création du groupe pour la France SelectItemGroup franceGroup = new SelectItemGroup("France", "Villes de France", false, villesFrance); //Allemagne SelectItem[] villesAllemagne = new SelectItem[2]; villesAllemagne[0] = new SelectItem("KehlValue", "Kehl :"); villesAllemagne[1] = new SelectItem("BerlinValue", "Berlin :"); //Création du groupe pour l’Allemagne SelectItemGroup allemagneGroup = new SelectItemGroup( "Allemagne", "Villes d'Allemagne", false, villesAllemagne); SelectItem londres = new SelectItem("LondresValue", "Londres :"); allGroupVilles.add(franceGroup); allGroupVilles.add(londres); allGroupVilles.add(allemagneGroup); Ce qui donne de manière graphique : http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 40 JSF - Framework Web 5.9. 41 / 108 RadioButton La balise <h:selectOneRadio> permet de définir une liste de boutons radio. Elle utilise le composant « HtmlSelectOneRadio » qui implémente « EditableValueHolder ». La liste des radios boutons va être construite en imbriquant des balises <f:selectItem> ou une balise <f:selectItems>, voir chapitre « 5.8.2 Checkbox multiples » pour le fonctionnement de ces balises. Attention cependant, car dans le cas de boutons radio, seul un bouton peut être coché à la fois, contrairement aux checkbox. La valeur du bouton radio sélectionné peut être associée à une primitive, une String, un Number ou n'importe quelle classe pour laquelle il existe un convertisseur de données. Exemple : <h:selectOneRadio layout="lineDirection" value="#{bean.color}> <f:selectItem itemValue="rouge" itemLabel="rouge"/> <f:selectItem itemValue="blanc" itemLabel="blanc"/> <f:selectItem itemValue="jaune" itemLabel="jaune"/> </h:selectOneRadio> La propriété référencée par l'attribut « value » doit être de même type que la valeur des items. Ici « #{bean.color} » doit être de type String, puisque la valeur des items est aussi de type String. 5.10. Liste Il existe 2 composants pour afficher une liste: • <h:selectOneListbox> pour afficher une liste avec un seul item sélectionnable (même fonctionnement que <h:selectOneRadio>). • <h:selectManyListbox> pour afficher une liste à choix multiples (même fonctionnement que <h:selectManyCheckBox>). L'attribut « size » vous permet de définir le nombre d'éléments que peut afficher la liste. Exemple : <h:selectManyListbox id="color" value="#{bean.colors}"> <f:selectItem itemValue="rouge" itemLabel=" Rouge:"/> <f:selectItem itemValue="blanc" itemLabel=" Blanc:"/> <f:selectItem itemValue="jaune" itemLabel="jaune"/> </h:selectManyListbox> Ce qui donne : <select id="color" size="3"> http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 41 JSF - Framework Web 42 / 108 <option value="rouge">Rouge:</option> <option value="blanc">Blanc:</option> <option value="jaune">Jaune:</option> </select> De manière graphique ça donne : 5.11. Combo Box Une combo box se créée avec la balise <h:selectOneMenu>. Son fonctionnement est similaire à la balise <h:selectOneRadio>, voir chapitre « 5.9 RadioButton ». Exemple : <h:selectOneMenu id="ville" value="#{bean.selectedVilles}"> <f:selectItems value="#{bean.allVilles}" /> </h:selectOneMenu> De manière graphique ça donne : 5.12. Image La balise <h:graphicImage> permet d’insérer une image. Elle possède les attributs : • • « url », définit l’adresse de l’image « alt », le descriptif de l’image <h:graphicImage url="url de l'image" alt="description"/> Il est possible de faire des images qui servent de liens, pour cela il suffit d’imbriquer une balise <h:graphicImage> dans une balise de lien (<h:outputLink> dans l’exemple qui suit) : <h:outputLink value="http://java.net"> <h:graphicImage value="java-dot-net.jpg" /> <h:outputText value="java.net" /> </h:outputLink> En HTML, ça donne: <a href="http://java.net"><img src="java.jpg" alt="" />java.net</a> http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 42 JSF - Framework Web 5.13. 43 / 108 Panel La balise <h:panelGroup> permet de regrouper plusieurs éléments JSF pour qu’ils soient considérés comme une seule balise. Certaines balises ne peuvent pas avoir qu’un seul enfant, dans ce cas, on utilise <h:panelGroup>. Exemple : <h:panelGroup> <h:outputText value="first child" /> <f:verbatim><br /></f:verbatim> <h:outputText value="second child" /> </h:panelGroup> Ce qui va produire le code HTML suivant : first child<br />second child Notez l’utilisation de la balise <f:verbatim> autour de la balise <br/>. Si on l’enlève, le code ne fonctionne pas sous JSF 1.1 mais marche toujours sous JSF 1.2. Exemple : <h:panelGroup> <h:outputText value="first child" /> <br /> <h:outputText value="second child" /> </h:panelGroup> Il donne le code HTML suivant : <br />first childsecond child La balise <br/> est affichée avant le reste. 5.14. Tableau Il existe 2 manières de définir un tableau en JSF : PanelGrid et DataTable. Il n'y a que la manière de créer le tableau qui diffère, le résultat final est le même. Les 2 balises sont <h:panelGrid> et <h:dataTable>. Elles possèdent les attributs communs suivants : • footerClass : liste des classes CSS (séparées par des espaces) pour le pied de page du tableau, • headerClass : liste des classes CSS (séparées par des espaces) pour l’en-tête du tableau, • captionClass (uniquement JSF 1.2) : liste des classes CSS (séparées par des espaces) pour le titre L’attribut « columnClasses » permet de définir une liste de style CSS pour chaque colonne. Les classes CSS d’une même colonne sont séparées par des espaces. Ce qui donne : class1.1 class1.2 class1.3 ... Dès que toutes les classes d’une colonne sont déclarées, il faut utiliser une virgule pour passer à la déclaration des classes de la colonne suivante. Ce qui donne class1.1 class1.2 class1.3, class2 class2.1, class3, class4 class4.1 class 4.2 ... http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 43 JSF - Framework Web 44 / 108 Dans ce cas, si le tableau possède 4 colonnes, les classes CSS « class1.1 », « class1.2 » et « class1.3 » seront pour la 1ère colonne, « class2 » et « class2.1 » pour la 2ème et ainsi de suite jusqu’à la 4ème colonne. Si la liste des classes CSS ne correspond pas au nombre de colonnes de votre tableau alors : • S’il y a plus de colonnes que de styles CSS, les styles CSS sont réutilisés en boucle. • S’il y a moins de colonnes que de styles CSS, les styles CSS en trop ne sont pas utilisés. L’attribut « rowClasses » permet de définir le style CSS des lignes et fonctionne de la même manière. 5.14.1. Tableau statique : PanelGrid Ce type de tableau se crée avec la balise <h:panelGrid>. Quand on utilise ce tableau, c’est que l’on connaît à l’avance son nombre de lignes et colonnes. Il n’est donc pas adapté pour itérer sur une liste et générer des lignes dynamiquement. Pour décrire le contenu du tableau, on va imbriquer des balises. Une balise imbriquée correspond à une case du tableau. La position de la case dans le tableau dépend de l’ordre de déclaration des balises. On va décrire les éléments à partir de la case tout en haut à gauche jusqu’à la case tout en bas à droite. Il n’y a aucune balise qui indique la fin ou le début d’une ligne. JSF va déterminer la position d’une case en prenant sa position dans la liste des balises imbriquées modulo le nombre de colonnes du tableau. Le nombre de colonnes se précise grâce à l’attribut « columns », par défaut il vaut 1. Exemple : <h:panelGrid border=”1” columns="2"> <h:outputText value="A" /> <h:outputText value="B" /> <h:outputText value="C" /> <h:outputText value="D" /> </h:panelGrid> Comme le tableau possède 2 colonnes, tous les 2 composants JSF va effectuer un retour à la ligne. Ce qui va donner de manière graphique : A B C D Si maintenant on met 3 colonnes, le retour à la ligne ne se fera que toutes les 3 balises : <h:panelGrid border="1" columns="3"> <h:outputText value="A" /> <h:outputText value="B" /> <h:outputText value="C" /> <h:outputText value="D" /> </h:panelGrid> Ce qui va donner de manière graphique : http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 44 JSF - Framework Web A B 45 / 108 C D 5.14.1.1. Regroupement d’éléments Si on veut utiliser plus d’une balise pour représenter une case, il faut utiliser la balise <h:panelGroup>. Exemple : <h:panelGrid columns="2" border="1" rules="all"> <h:outputText value="A" /> <h:panelGroup> <h:outputText value="B" /> <f:verbatim><br /></f:verbatim> <h:outputText value="B bis" /> </h:panelGroup> <h:outputText value="C" /> <h:outputText value="D" /> </h:panelGrid> Le code HTML correspondant est : <table border="1" rules="all"> <tbody> <tr> <td>A</td> <td>B<br />B bis</td> </tr> <tr> <td>C</td> <td>D</td> </tr> </tbody> </table> 5.14.1.2. En-tête et pied de page Une en-tête ou un pied de page se déclare grâce à la balise <f:facet>. Elle possède l’attribut « name » qui indique de quelle partie il s’agit. Les valeurs possibles sont : • « header », s’il s’agit de l’en-tête, • « footer », s’il s’agit du pied de page. • « caption », s’il s’agit du titre (uniquement en JSF 1.2) La balise <f:facet> n’accepte qu’un seul enfant, il faut utiliser la balise <h:panelGroup> pour imbriquer plusieurs balises. Exemple : <h:panelGrid border="1" columns="2" rules="all"> <f:facet name="header"> <h:outputText value="En-tête" /> </f:facet> <f:facet name="footer"> <h:panelGroup> <h:outputText value="Pied " /> http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 45 JSF - Framework Web 46 / 108 <h:outputText value="de page" /> </h:panelGroup> </f:facet> <!--Ne marche pas en JSF 1.1--> <f:facet name="caption"> <h:outputText value="caption" /> </f:facet> <h:outputText value="A" /> <h:outputText value="B" /> <h:outputText value="C" /> <h:outputText value="D" /> </h:panelGrid> Ce qui donne le code HTML : <table border="1" rules="all"> <thead> <tr> <th colspan="2" scope="colgroup">En-t&ecirc;te</th> </tr> </thead> <tfoot> <tr> <td colspan="2">Pied de page</td> </tr> </tfoot> <tbody> <tr> <td>A</td> <td>B</td> </tr> <tr> <td>C</td> <td>D</td> </tr> </tbody> </table> Et en graphique: 5.14.2. Tableau dynamique : DataTable La balise <h:dataTable> permet de générer un tableau dynamiquement. Elle sert à parcourir une liste d’éléments. Il est possible de parcourir les objets suivants : • une liste de java beans, http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 46 JSF - Framework Web • un tableau de java beans, • un java bean, • un objet de type javax.faces.model.DataModel, • un objet de type java.sql.ResultSet, • un objet de type javax.servlet.jsp.jstl.sql.ResultSet, • un objet de type javax.sql.RowSet. 47 / 108 L’attribut « value » permet de préciser quel élément on veut parcourir. L’attribut « var » est le nom de la variable qui va contenir l’objet sur lequel on se trouve pendant le parcours. Ainsi le début de la balise est sous cette forme: <h:dataTable value="#{bean.listeElements}" var="itemDeLaListe" /> Pour définir le contenu du tableau, il faut se servir de la balise <h:column>. On va retrouver autant de ces balises qu’il y a de colonne. Toutes les balises imbriquées à celle-ci vont définir le contenu d’une case. <h:dataTable id="items" value="#{bean.listeElements}" var="itemDeLaListe"> <h:column> <%-- 1ère colonne--%> <h:outputText value="#{itemDeLaListe.property1" /> </h:column> <h:column> <%-- 2ème colonne--%> <h:outputText value="#{itemDeLaListe.property2}" /> </h:column> </h:dataTable> Si la liste « bean.listeElements » contient 5 éléments, on va se retrouver avec un tableau de 5 lignes et de 2 colonnes. Si on ne souhaite parcourir qu’une partie de la liste on peut utiliser l’attribut « first » pour définir le premier élément à partir duquel on veut commencer le parcours et « rows » pour le nombre d’éléments à parcourir. 5.14.2.1. En-tête et pied de page Comme pour les tableaux PanelGrid, les en-têtes, pieds de page et titres se définissent à l’aide de la balise <f:facet>. Pour définir l’en-tête (pied de page) d’une colonne, il faut imbriquer la balise <f:facet> dans une balise <h:column>. Pour définir l’en-tête (pied de page) pour tout le tableau, il faut placer la balise <f:facet> directement sous <h:dataTable>. Exemple : <h:dataTable id="items" value="#{bean.listeElements}" var="itemDeLaListe"> <h:column> <%-- 1ère colonne--%> <f:facet name="header"> <%-- En-tête spécifique à la colonne--%> http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 47 JSF - Framework Web 48 / 108 <h:outputText value="Property 1" /> </f:facet> <h:outputText value="#{itemDeLaListe.property1}" /> </h:column> <h:column> <%-- 2ème colonne--%> <f:facet name="header"> <%-- En-tête spécifique à la colonne--%> <h:outputText value="Property 2" /> </f:facet> <h:outputText value="#{itemDeLaListe.property2}" /> </h:column> <%-- pied de page général--%> <f:facet name="footer"> <h:outputText value="Pied de page"/> </f:facet> </h:dataTable> 5.14.2.2. Exemple : affichage d’une table d’une base de donnée Finissons par un exemple qui va illustrer comment afficher le contenu d’une table d’une base de données à l’aide d’un tableau. Nous allons commencer par créer le JavaBean qui va lire le contenu de la base. Quand on l’instancie, il fait une simple requête de type « SELECT » sur une table, puis il stocke le résultat dans un objet de type ResultSet. C’est ce dernier qui sera lu directement par la balise <h:dataTable>. Vous remarquerez ici que l’on n’utilise pas une connexion fournie par le serveur d’application. En effet, le but ici est simplement de montrer comment lire un ResultSet depuis un tableau en JSF. package com.labosun; public class DataBaseBean { private ResultSet personRS; public DataBaseBean() { initConnection(); } //Getter et setter pour personRS public ResultSet getPersonRS() { return personRS; } public void setPersonRS(ResultSet personRS) { this.personRS = personRS; } private void initConnection() { try { Class.forName("com.mysql.jdbc.Driver").newInstance(); Connection cnx = DriverManager.getConnection( "jdbc:mysql://localhost/jsfCours", "login", "pass"); Statement st = cnx.createStatement(); this.personRS = st.executeQuery("select * from person"); http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 48 JSF - Framework Web 49 / 108 } catch (Exception e) { //Gestion des exceptions (non détaillé ici) } } } Voici maintenant la déclaration du bean dans le fichier de config. <managed-bean> <managed-bean-name>dbBean</managed-bean-name> <managed-bean-class>com.labosun.DataBaseBean</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> </managed-bean> Enfin le code JSF qui va nous permettre d’utiliser notre ResultSet. L’attribut « value » de la balise <h:dataTable> prend directement le ResultSet en paramètre. Ensuite la valeur de chaque ligne va être accessible sous forme d’un tableau. Ce qui va permettre d’accéder à une colonne comme s’il s’agissait d’une propriété. <h:dataTable value="#{dbBean.personRS}" var="personDB" rules="all" border="1"> <h:column> <f:facet name="header"> <h:outputText value="Nom" /> </f:facet> //Accès à la colonne lastName (notation sous forme de tableau) <h:outputText value="#{personDB['lastName']}" /><br /> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Prénom" /> </f:facet> //Accès à la colonne firstName (notation pointée) <h:outputText value="#{personDB.firstName}" /> </h:column> </h:dataTable> 5.15. Message d’erreur Lorsqu’une erreur arrive dans une page (formulaire non valide, champ requis manquant, …) un message qui contient un détail de l’erreur est créée. Ces messages peuvent ensuite être affichés grâce aux balises <h:message> et <h:messages>. Le mécanisme de création et d’utilisation des messages est expliqué dans le chapitre « 8.2 La gestion des messages ». Ici, nous verrons seulement comment les afficher. Les messages possèdent une importance (propriété « severity ») qui peut prendre les valeurs suivantes : • « SEVERITY_INFO », message d’information, • « SEVERITY_WARN », une erreur peut avoir eu lieu, • « SEVERITY_ERROR », pour indiquer une erreur, • « SEVERITY_FATAL », erreur très grave http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 49 JSF - Framework Web 50 / 108 Il existe 2 types de messages, les messages spécifiques à un composant et les messages globaux. • La balise <h:message> affiche les messages relatifs à un composant. L’attribut « for » doit renseigner l’identifiant du composant pour lequel on veut afficher les messages. • La balise <h:messages> permet d’afficher les messages globaux et les messages spécifiques à un composant. Ces 2 balises possèdent les attributs : Attribut Type Valeur par défaut Description showDetail boolean false Indique si le détail des messages doit être affiché. showSummary boolean true Indique si le résumé des messages doit être affiché. La balise <h:messages> possède en plus les attributs suivants : Attribut Type Valeur par défaut Description globalOnly boolean false Indique si les messages spécifiques à un composant doivent être affichés. layout String list Permet de définir le rendu des différents messages. Les valeurs possibles sont : • « list » pour afficher les messages sous forme de liste HTML, • « table » pour afficher les messages dans un tableau HTML. Exemple : <h:inputText id="number"> <f:validateLongRange minimum="1" maximum="10" /> </h:inputText> <h:message for="number"/> <h:commandButton id="submit" action="success"/> La balise <f:validateLongRange> permet de tester si le nombre rentré par l’utilisateur est compris entre 1 et 10. Si la valeur rentrée par l’utilisateur n’est pas comprise entre 1 et 10, un message s’affiche. L’aspect visuel des messages peut être changé en fonction de leur importance grâce aux attributs CSS : Nom Description http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 50 JSF - Framework Web errorClass Classe CSS à utiliser pour les messages d’un niveau « ERROR ». fatalClass Classe CSS à utiliser pour les messages d’un niveau « FATAL ». infoClass Classe CSS à utiliser pour les messages d’un niveau « INFO ». warnClass Classe CSS à utiliser pour les messages d’un niveau « WARN ». 51 / 108 Il existe également les attributs « errorStyle », « fatalStyle », « infoStyle » et « warnStyle » pour définir le style directement. Il est possible d’afficher n’importe quel composant seulement s’il y a des erreurs grâce à l’attribut « rendered ». <h:outputText value="Il y a une erreur" facesContext.maximumSeverity}" /> rendered="#{! empty L’expression « #{! empty facesContext.maximumSeverity} » permet de tester si la liste des messages d’erreur est vide. http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 51 JSF - Framework Web 52 / 108 6. Configuration Serveur La configuration de JSF se fait dans le fichier « faces-config.xml » qui doit être placé dans le répertoire WEB-INF d’une application Web. La structure basique de ce fichier en JSF 1.1 est : <?xml version="1.0"?> <!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN" "http://java.sun.com/dtd/web-facesconfig_1_1.dtd"> <faces-config> </faces-config> Pour JSF 1.2, le fichier à la forme suivante : <?xml version='1.0' encoding='UTF-8'?> <faces-config xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd" version="1.2"> </faces-config> Toute la configuration va ensuite se retrouver entre les balises <faces-config>. 6.1. Déclaration des JavaBeans Pour configurer tous les JavaBeans qui se trouvent dans les pages JSF on utilise une ou plusieurs déclarations du genre : <managed-bean> <managed-bean-name>nom du bean dans la page JSF</managed-bean-name> <managed-bean-class>class du bean</managed-bean-class> <managed-bean-scope>( application|session|request|none)</managed-bean-scope> <managed-property> <property-name>nom de la propriété</property-name> <property-class>type de la propriété</property-class> <value>valeur par défaut</value> </managed-property> </managed-bean> • « managed-bean-name » référence le nom du bean dans une page JSF. Si plusieurs pages utilisent le même nom, la configuration sera appliquée à toutes les pages. • « managed-bean-class » est le nom de la classe du bean. Attention le package dans lequel se trouve le JavaBean fait partie du nom de la classe. • « managed-bean-scope » définit la portée du bean« managed-property » permet d’initialiser une propriété d’un bean. Les valeurs possibles pour la portée sont : • « application » initialise le bean pour toute l’application. http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 52 JSF - Framework Web • « session » pour toute la session • « request » uniquement pour la durée d’une requête • « none » le bean est crée à chaque fois qu’on l’utilise. 53 / 108 6.1.1. Déclaration des List Une liste se déclare grâce à la balise <list-entries>. Il suffit ensuite d'imbriquer autant de balises <value> qu'il y a de valeur. La valeur null peut être obtenu grâce à la balise <null-value>. Si vous souhaitez préciser le type d'objet que contient la liste, il faut utiliser la balise <value-class>. Exemple: <managed-bean> <managed-bean-name>villeList</managed-bean-name> <managed-bean-class>java.util.ArrayList</managed-bean-class> <managed-bean-scope>application</managed-bean-scope> <list-entries> <value-class>java.lang.String</value-class> <value>Strasbourg</value> <value>Bordeaux</value> <value>Paris</value> <value>Nice</value> <null-value /> </list-entries> </managed-bean> 6.1.2. Déclaration des Maps Une Map se déclare grâce à la balise <map-entries>. Pour chaque entrée dans la map, il faut une balise <map-entry>. Cette balise ne contient que 2 sous éléments, la balise <key> pour représenter la clef et la balise <value> pour la valeur qui lui est associée. Tout comme pour les listes, il est possible de préciser le type d'éléments que contient la map. La balise <key-class> précise la classe des clefs utilisées et la balise <value-class> précise la classe des valeurs utilisées. Ces 2 balises se placent directement en tant que balise fille de <map-entries>. Enfin, il est possible d'utiliser la balise <null-value> pour utiliser la valeur nulle. Exemple: <managed-bean> <managed-bean-name>dicoMap</managed-bean-name> <managed-bean-class>java.util.HashMap</managed-bean-class> <managed-bean-scope>application</managed-bean-scope> <map-entries> <key-class>classe des clefs</key-class> <value-class>classe des valeurs</value-class> <!-- Première entrée --> <map-entry> <key>Valeur de la clef1</key> <value>Valeur associée à la clef</value> </map-entry> <!-- Deuxième entrée --> <map-entry> http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 53 JSF - Framework Web 54 / 108 <key>Valeur de la clef2</key> <value>Valeur associée à la clef</value> </map-entry> </map-entries> </managed-bean> 6.1.3. Exemple : Déclaration d'une liste de SelectItem Maintenant que nous savons déclarer des listes, voyons comment créer une liste de SelectItem. Tout d'abord il faut déclarer une liste qui va contenir uniquement des objets de type « javax.faces.model.SelectItem ». Ensuite il suffit de déclarer chaque élément de la liste comme étant un bean. Pour finir il faut déclarer autant de bean de type « javax.faces.model.SelectItem » qu'il y a d'éléments. <!-- Déclaration de la liste --> <managed-bean> <managed-bean-name>villes</managed-bean-name> <managed-bean-class>java.util.ArrayList</managed-bean-class> <managed-bean-scope>application</managed-bean-scope> <list-entries> <value-class>javax.faces.model.SelectItem</value-class> <value>#{ville0}</value> <value>#{ville1}</value> </list-entries> </managed-bean> <!-- Déclaration du premier bean --> <managed-bean> <managed-bean-name>ville0</managed-bean-name> <!-- La classe est javax.faces.model.SelectItem --> <managed-bean-class>javax.faces.model.SelectItem</managed-bean-class> <managed-bean-scope>application</managed-bean-scope> <!-- Les 2 seules propriétés à définir sont value et label --> <managed-property> <property-name>value</property-name> <value>Stras</value> </managed-property> <managed-property> <property-name>label</property-name> <value>Strasbourg</value> </managed-property> </managed-bean> <!-- Déclaration du deuxième bean --> <managed-bean> <managed-bean-name>ville1</managed-bean-name> <managed-bean-class>javax.faces.model.SelectItem</managed-bean-class> <managed-bean-scope>application</managed-bean-scope> <managed-property> <property-name>value</property-name> <value>Lyon</value> </managed-property> <managed-property> <property-name>label</property-name> <value>Lyon</value> </managed-property> </managed-bean> http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 54 JSF - Framework Web 55 / 108 Comme on peut le voir ici, ce type de déclaration est très lourd. Il est plus court de déclarer les objets directement dans un JavaBean. http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 55 JSF - Framework Web 6.2. 56 / 108 Navigation La navigation du site se définit à l’aide de plusieurs règles de navigation. Chaque règle définit toutes les pages de destination possibles à partir d’une page source. Une règle se définit à l’aide de la balise <navigation-rule>. <navigation-rule> <from-view-id>/Page de départ</from-view-id> <navigation-case> <from-outcome>chaîne renvoyé par la page</from-outcome> <to-view-id>/Page de destination</to-view-id> </navigation-case> </navigation-rule> La balise « from-view-id » définit la page source. On peut ensuite inclure autant de balises « navigation-case » que nécessaire, chaque balise définit une page de destination possible. La balise « from-outcome » est la chaîne qui sera renvoyée par un bouton ou un lien. Il est possible d’utiliser des espaces dans cette chaîne. Enfin la balise « to-view-id » est la page de destination. Prenons l’exemple suivant, on est sur la page « index.jsp » et l’on veut se rendre à la page « achat.jsp ». On peut commencer par écrire la règle de navigation : <navigation-rule> <from-view-id>/index.jsp</from-view-id> <navigation-case> <from-outcome>success</from-outcome> <to-view-id>/achat.jsp</to-view-id> </navigation-case> </navigation-rule> L’utilisation du « / » au début des urls permet de faire référence à la racine du site. Il suffit maintenant d’insérer un composant qui renvoie la chaîne « achat » dans la page « index.jsp ». Le renvoi de chaîne d’action se fait grâce à l’attribut « action ». Exemple avec un lien: <h:form> <h:commandLink action="success" value="Vers la page achat"/> </h:form> Exemple avec un bouton: <h:form> <h:commandButton action="success" value="Vers la page achat"/> </h:form> A noter que cette déclaration fonctionne aussi bien pour un mapping de Servlet en mode préfixe ou suffixe. Ainsi si on utilise le mapping suivant : <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>/faces/*</url-pattern> </servlet-mapping> Le préfixe « /faces/ » sera automatiquement rajouté dans les URLs de navigation. http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 56 JSF - Framework Web 57 / 108 Si on utilise le mapping : <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.jsf</url-pattern> </servlet-mapping> On accèdera bien aux pages avec l’extension « .jsf ». 6.2.1. Mapping global Il est également possible de définir une règle de navigation pour un ensemble de pages. Pour cela il faut utiliser le caractère « * » pour représenter un ensemble de pages. Ce qui donne par exemple : <navigation-rule> <from-view-id>/*</from-view-id> <navigation-case> <from-outcome>error</from-outcome> <to-view-id>/error.jsp</to-view-id> </navigation-case> </navigation-rule> Dans ce cas, toutes les pages qui retournent la chaîne « error » renverront sur la page « error .jsp». Une autre solution pour faire un mapping global, est de ne pas mettre de balise <from-view-id>. 6.2.2. Chaînes d’action Voyons maintenant plus en détail les différentes manière de configurer la navigation. Il existe en fait 2 balises pour définir les chaînes d’actions : <from-action> et <from-outcome>. Elles peuvent être utilisées seules ou ensemble, ce qui nous fait 3 manières de déclarer un cas de navigation. • Si on ne spécifie que « from-outcome », la chaîne peut correspondre soit à la valeur de l’attribut action soit à une chaîne retournée par une méthode qui gère la navigation. • Si on spécifie les 2. Dans ce cas, la navigation ne sera vraie que si la méthode définie dans « from-action » retourne la chaîne spécifiée par « from-outcome ». • Si on ne spécifie que « from-action », la valeur doit être une référence à une méthode. Quelque soit la valeur retournée par cette méthode, la navigation sera vraie. Les chaînes d’actions couramment utilisées pour la balise « from-outcome » sont : Outcome Signification success Fonctionnement correct, passage à la page suivante. failure Erreur dans la page, passage à la page d’erreur logon L’utilisateur doit s’authentifier, passage à la page de login no results La recherche n’a donné aucun résultat. Retour à la page de recherche. http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 57 JSF - Framework Web 6.2.2.1. 58 / 108 From-outcome C’est la balise que nous avons utilisée pour les exemples précédents. On peut également utiliser une méthode d’un JavaBean pour retourner la chaîne d’action. Il est possible d’utiliser n’importe quelle méthode de n’importe quel JavaBean. La seule contrainte à respecter est que la méthode ait le prototype suivant : public String methodName(); Il suffit ensuite d’écrire la balise JSF de la manière suivante : <h:commandLink action="#{bean.method}" value="Vers la page xxx"/> Prenons un exemple, on déclare la règle suivante : <navigation-rule> <from-view-id>/index.jsp</from-view-id> <navigation-case> <from-outcome>success</from-outcome> <to-view-id>/achat.jsp</to-view-id> </navigation-case> </navigation-rule> On peut ensuite écrire une méthode qui retourne « success ». public class NaviBean { public String getSuccessString() { return "success"; } } On va ensuite déclarer notre lien de la manière suivante dans la page « index.jsp ». <h:form> <h:commandLink value="Vers la page achat" action="#{navi.getSuccessString}"/> </h:form> Ici « navi » est le nom du bean précédent. 6.2.2.2. From-action et from-outcome Illustrons le fonctionnement de « from-action » avec « from-outcome » avec le cas le plus simple : <navigation-rule> <from-view-id>/index.jsp</from-view-id> <navigation-case> <from-action>#{navi.valideAchat}</from-action> <from-outcome>achat</from-outcome> <to-view-id>/achat.jsp</to-view-id> </navigation-case> <navigation-case> <from-action>#{navi.valideAchat}</from-action> <from-outcome>mustLogin</from-outcome> <to-view-id>/login.jsp</to-view-id> </navigation-case> </navigation-rule> Les 2 balises <from-action> sont identiques, ce qui va permettre de déterminer la navigation, c’est la balise <from-outcome> qui va déterminer la page de destination. Si la méthode « valideAchat » http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 58 JSF - Framework Web 59 / 108 retourne « achat », on va sur la page achat, si elle retourne « mustLogin », on va sur la page de login. Si elle retourne autre chose alors le cas n’est pas traité et on reste sur la même page. Pour utiliser ces règles de navigation, il faut déclarer une action qui référence une méthode. Exemple : <h:commandLink value="Valide les achats" action="#{navi.valideAchat}"/> Voici un exemple de méthode qui retourne aléatoirement la chaîne « achat » ou « mustLogin » ce qui permet de tester notre règle de navigation. public class NaviBean { public String valideAchat() { Random r = new Random(); int choice = r.nextInt(2); if( choice == 0) { return "achat"; } else { return "mustLogin"; } } } 6.2.2.3. From-action Avec cette balise, la navigation se base uniquement sur la méthode d’action qui a été appelée. Par exemple avec la déclaration suivante : <navigation-rule> <from-view-id>/index.jsp</from-view-id> <navigation-case> <from-action>#{navi.valideAchat}</from-action> <to-view-id>/achat.jsp</to-view-id> </navigation-case> </navigation-rule> Ici, peu importe la chaîne de retour de la méthode, on va dans tous les cas sur la page « achat ». Il suffit ensuite de déclarer un lien de la manière suivante : <h:commandLink action="#{navi.valideAchat}" /> 6.2.3. Exemple Finissons cette partie par un exemple de site constitué de plusieurs pages. Le but de ce mini site est de gérer le contenu d’une liste de String. Pour cela on utilisera un bean qui contiendra un objet de type List<String>. Le site sera constitué : • « list.jsp » : page principale, celle-ci permet de lister les éléments. Elle possède un lien vers la page de suppression, de détail et de création. • « detail.jsp » page de détail, elle affiche le détail d’un élément, elle possède un lien vers la page de suppression, et la page principale. • « delete.jsp » page de suppression, elle affiche un message de confirmation et retourne ensuite à la page principale. http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 59 JSF - Framework Web • 60 / 108 « new.jsp » page de création, elle permet de créer un nouvel élément. Elle possède un lien vers la page principale. Pour savoir quel élément on doit supprimer lorsque l’on passe de la page principale à la page de suppression, on utilisera le passage de paramètre comme vu lors du chapitre « 5.6.3 Paramètres ». Voici ce que donnent les règles de navigation : <navigation-rule> <from-view-id>/list.jsp</from-view-id> <navigation-case> <!-- Lien vers la page de détail --> <from-outcome>edit</from-outcome> <to-view-id>/edit.jsp</to-view-id> </navigation-case> <navigation-case> <!-- Lien vers la page de suppression --> <from-outcome>delete</from-outcome> <to-view-id>/delete.jsp</to-view-id> </navigation-case> <navigation-case> <!-- Lien vers la page de création --> <from-outcome>new</from-outcome> <to-view-id>/new.jsp</to-view-id> </navigation-case> </navigation-rule> <navigation-rule> <from-view-id>/delete.jsp</from-view-id> <navigation-case> <!-- Lien vers la page principale --> <from-outcome>success</from-outcome> <to-view-id>/list.jsp</to-view-id> </navigation-case> </navigation-rule> <navigation-rule> <from-view-id>/new.jsp</from-view-id> <navigation-case> <!-- Lien vers la page de création --> <from-outcome>success</from-outcome> <to-view-id>/list.jsp</to-view-id> </navigation-case> </navigation-rule> <navigation-rule> <from-view-id>/edit.jsp</from-view-id> <navigation-case> <!-- Lien vers la page de suppression --> <from-outcome>delete</from-outcome> <to-view-id>/delete.jsp</to-view-id> </navigation-case> <navigation-case> <!-- Lien vers la page principale --> <from-outcome>list</from-outcome> <to-view-id>/list.jsp</to-view-id> </navigation-case> </navigation-rule> Le bean qui va servir dans les pages est le suivant : public class NaviExempleBean { private List<String> elements; //Liste des éléments private String newElt; //Sert pour l'ajout de nouvel élément http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 60 JSF - Framework Web 61 / 108 /** * Initialise une liste d'éléments avec des valeurs par défaut */ public NaviExempleBean() { this.elements = new ArrayList<String>(); elements.add("salade"); elements.add("jambon"); elements.add("pêche"); elements.add("tomate"); elements.add("patate"); } //Getters et setters ... /** * Créé un nouvel élément et l'ajoute à la liste. * @return success si la création réussie, failure sinon */ public String create() { if (newElt != null) { this.elements.add(newElt); newElt = null; return "success"; } else { return "failure"; } } /** * Supprime l'élément dont la valeur est égale au paramètre de requête "idElt". * @return success pour indiquer la fin de la suppression */ public String delete() { String element = FacesContext.getCurrentInstance() .getExternalContext().getRequestParameterMap().get("idElt"); FacesMessage fm; if (element != null) { elements.remove(element); fm = new FacesMessage(FacesMessage.SEVERITY_INFO, "Suppression réussie", ""); } else { fm = new FacesMessage(FacesMessage.SEVERITY_INFO, "Suppression râtée", ""); } FacesContext.getCurrentInstance().addMessage(null, fm); return "success"; } } Et sa déclaration : <managed-bean> <managed-bean-name>naviEx</managed-bean-name> <managed-bean-class>com.labosun.NaviExempleBean</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> </managed-bean> http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 61 JSF - Framework Web 62 / 108 La page « list.jsp » : <f:view> <h:form> <h1>Liste</h1> <!-- Permet d'afficher les messages d'erreurs --> <h:messages /> <h:dataTable value="#{naviEx.elements}" var="elt" rules="all" border="1"> <h:column> <f:facet name="header"> <h:outputText value="Nom" /> </f:facet> <h:outputText value="#{elt}" /> </h:column> <h:column> <!-- Lien pour supprimer --> <h:commandLink value="supprimer" action="delete"> <!-- on passe le paramètre idElt à la page de suppression --> <f:param name="idElt" value="#{elt}" /> </h:commandLink> </h:column> <h:column> <!-- Lien pour éditer --> <h:commandLink value="détail" action="edit"> <!-- on passe le paramètre idElt à la page de détail--> <f:param name="idElt" value="#{elt}" /> </h:commandLink> </h:column> </h:dataTable> <h:commandLink value="Créer un nouvel élément" action="new" /> </h:form> </f:view> Voici ce qu’elle donne dans un navigateur : La page « edit.jsp » : <f:view> <h:form> http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 62 JSF - Framework Web 63 / 108 <!-- On utilise le paramètre idElt pour savoir quel élément il faut afficher--> <h:outputText value="Détail de #{param['idElt']}" /><br /> <h:commandLink value="Supprimer cet élément" action="delete"> <f:param name="idElt" value="#{param['idElt']}" /> </h:commandLink> <h:commandLink value="Retour à la liste" action="list" /> </h:form> </f:view> Voici le résultat : La page « new.jsp » : <f:view> <h:form> <h1>Nouvel élément</h1> <h:inputText id="name" value="#{naviEx.newElt}"> <h:outputLabel for="name" value="Nom:" /> </h:inputText> <h:commandButton value="Valider" action="#{naviEx.create}" /><br /> <h:commandLink value="Annuler" action="success" /> </h:form> </f:view> Voici le résultat en image : La page « delete.jsp » : <f:view> <h:form> <!-- On utilise le paramètre idElt pour savoir quel élément il faut supprimer--> <h:outputText value="Etes-vous sûr de vouloir supprimer l'élément: #{param['idElt']} ?" /> <h:commandLink value="Oui" action="#{naviEx.delete}"> <!-- on repasse toujours le paraètre idElt pour que le bean puisse savoir quel élément supprimer --> <f:param name="idElt" value="#{param['idElt']}" /> </h:commandLink> <h:commandLink value="Non" action="success" /> </h:form> </f:view> http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 63 JSF - Framework Web 6.3. 64 / 108 Paramètres de contexte Le fonctionnement de conteneur JSF peut être modifié à l'aide de paramètres de « context ». Ces paramètres doivent être modifiés dans le fichier « web.xml ». <context-param> <param-name>Nom du paramètre</param-name> <param-value>Valeur du paramètre</param-value> </context-param> Les paramètres standard sont: javax.faces.CONFIG_FILES Liste des fichiers (séparé par des virgules) dans lesquels il faut chercher des paramètres de configuration. Par défaut un conteneur JSF cherchera uniquement le fichier « faces-config.xml ». javax.faces.DEFAULT_SUFFIX Extension des fichiers qui contient le code JSF. Par défaut « .jsp » javax.faces.LIFECYCLE_ID Permet de définir l'instance de LifeCycle pour gérer tout le cycle de vie de l'application. L'instance par défaut est: LifecycleFactory.DEFAULT_LIFECYCLE javax.faces.STATE_SAVING_METHOD Permet de définir le mode de sauvegarde des données. Les 2 valeurs valides sont: • « server », les informations sont stockées dans la session de l'utilisateur • « client », les informations sont stockées côté client sous la forme d’un champ caché Par défaut, « server » est utilisé. http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 64 JSF - Framework Web 65 / 108 7. Les comportements 7.1. ActionListener Tous les composants qui implémentent « ActionSource » (UICommand) peuvent générer un événement. C'est-à-dire qu'une méthode est appelée lorsque l'utilisateur clique sur un élément. L'événement est traité côté serveur, lorsque le client envoie la page au serveur. Voici pour rappel le cycle de vie d’une requête JSF : Les événements de type ActionListener sont traités pendant la phase « Invocation de l’application ». Si le composant qui déclare l’événement a l’attribut « immediate » à true, alors l’événement est traité pendant la phase « Application des paramètres de la requête ». Attention cependant, car pendant cette phase, tous les éléments ne sont pas encore à jour (les javabeans sont mis à jour seulement pendant la phase « Mise à jour du modèle »). L’ordre de mise à jour des éléments est l’ordre de déclaration des balises. Il existe 2 manières de prendre en charge l’événement côté serveur. La première solution est d’associer une méthode d’un JavaBean générique. La deuxième est de créer une implémentation de l’interface « ActionListener ». 7.1.1. Méthode générique Pour créer une méthode qui gère un événement, il faut qu’elle ait le prototype suivant : public void nomMethode(javax.faces.event.ActionEvent e); La méthode peut se trouver dans n'importe quel bean, mais il doit être référencé en tant que bean géré par le conteneur. Pour utiliser la méthode il suffit de faire référence à elle grâce à l’attribut « actionListener ». Exemple : <h:commandButton value="Send" actionListener="#{nomDuBean.nomMethode}" /> 7.1.2. Interface ActionListener Il est également possible de créer une classe qui implémente « javax.faces.event.ActionListener ». Il y a juste la méthode « processEvent(ActionEvent) » à implémenter. Exemple : package listeners; public class GereEvent implements ActionListener { public void processAction(ActionEvent actionEvent) { //Gestion de l'événement } http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 65 JSF - Framework Web 66 / 108 } Cet ActionListener peut se référencer de 2 manières différentes. Il est possible d’utiliser l’attribut « actionListener » comme vu précédemment. Mais on peut aussi utiliser la balise. <f:actionListener>. L’attribut « type » de cette balise permet de préciser la classe de l’ActionListener qu’on utilise. Exemple : <h:commandLink value="avec l’interface"> <f:actionListener type="listeners.GereEvent" /> </h:commandLink> 7.2. ValueChange Listener Les composants qui implémentent « EditableValueHolder », c’est-à-dire toutes les classes filles de UIInput (UISelectBoolean, UISelectMany, UISelectOne) possèdent une valeur éditable par l’utilisateur. Lorsque que la page sera envoyée au serveur, toutes les valeurs qui ont été modifiées vont générer un événement côté serveur. La gestion de cet événement est similaire à ActionListener. Voici pour rappel le cycle de vie d’une requête JSF : Les événements de type ValueChangeListener sont traités pendant la phase « Validation des entrées utilisateurs ». Si le composant qui déclare l’événement a l’attribut « immediate » à true, alors l’événement est traité pendant la phase « Application des paramètres de la requête ». Si c’est le cas, alors toutes les opérations de conversions et de validation liées à ce composant vont être réalisées avant la gestion du ValueChangeListener. Attention cependant, car pendant cette phase, tous les éléments ne sont pas encore à jour (les javabeans sont mis à jour seulement pendant la phase « Mise à jour du modèle »). L’ordre de mise à jour des éléments est l’ordre de déclaration des balises. 7.2.1. Méthode générique Une méthode qui gère l’événement doit avoir le prototype suivant : public void check(javax.faces.event.ValueChangeEvent event); Le JavaBean qui déclare la méthode doit être référencé en tant que JavaBean géré par le conteneur. La méthode peut ensuite être référencée grâce à l’attribut « valueChangeListener ». <h:inputText valueChangeListener="#{nomDuBean.nomMethode}" /> 7.2.2. Interface ValueChangeListener L’autre solution consiste à implémenter « javax.faces.event.ValueChangeListener » et de redéfinir la méthode « processValueChange (ValueChangeEvent) ». Exemple : package listeners; http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 66 JSF - Framework Web 67 / 108 public class BeanValueChangeListener implements ValueChangeListener { public void processValueChange(ValueChangeEvent event) { //Gestion de l'événement } } Cet écouteur peut se référencer soit grâce à l’attribut « valueChangeListener » comme vu précédemment, soit grâce à la balise. <f:valueChangeListener>. L’attribut « type » de celle-ci permet de préciser la classe de l’écouteur qu’on utilise. Exemple : <h:inputText value="valeur"> <f:valueChangeListener type="listeners.BeanValueChangeListener" /> </h:inputText > 7.3. Conversions de données Les composants JSF qui implémentent ValueHolder (toutes les classes filles de UIOutput) possèdent une valeur. Cette valeur peut être une date, un nombre, un email, … Il n’y a pas de restrictions sur la valeur qu’on peut associer à un composant à condition que la valeur puisse être convertie d’un objet vers un String et inversement. Cette transformation est nécessaire, car lors de la phase « Application des paramètres de la requête », la Servlet JSF doit être capable de reconstruire l’objet qui représente la valeur à partir de la requête cliente (qui est sous forme de String). Ensuite, lors de la phase du rendu, l’objet doit être traduit en String. JSF gère tout seul la conversion des primitives et des objets standard de Java : Boolean, Number. Toutes les opérations de conversions sont effectuées lors de la phase « Application des paramètres de la requête ». Ce principe de conversion permet également de personnaliser l’affichage des objets. Par exemple une date peut s’afficher sous la forme « JJ/MM/AAAA » ou alors avec le jour en toutes lettres. Les erreurs de conversions peuvent êtres affichés avec la balise <h:message>. 7.3.1. Conversion standard : Date La balise <f:convertDateTime> assure la conversion Date - String. Par défaut, une date est affichée de la manière suivante : « Sat Jul 22 12:56:12 CEST 2006 » ce qui correspond au samedi 22 juillet 2006. La balise <f:convertDateTime> possède les attributs suivants : Attribut Type Description dateStyle String Définit le format de la date. Les valeurs sont : default, short, medium, long et full http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 67 JSF - Framework Web 68 / 108 Pour la date du samedi 22 juillet 2006, les différentes valeurs sont : timeStyle String • Default: « Sat Jul 22 13:05:30 CEST 2006 » • short: « 22/07/06 » • medium: « 22 juil. 2006 » • long: « 22 juillet 2006 » • full: « samedi 22 juillet 2006 » Définit le format du temps. Les valeurs sont : default, short, medium, long et full locale String Locale ou La locale pour représenter la date timeZone String ou Définit la zone horaire pour laquelle on représente la date. TimeZone pattern String Permet de définir un format de date personnalisé. Il faut utiliser un pattern qui à une forme définie dans la documentation de la classe « SimpleDateFormat » de J2SE. S’il cet attribut est défini, il est utilisé à la place de dateStyle et timeStyle type String Permet de préciser si la valeur à représenter est une date, un temps ou les 2. Les valeurs sont : date (par défaut), time et both. Exemple d’utilisation : <h:inputText value="#{bean.date}"> <f:convertDateTime dateStyle="full" /> </h: inputText > Voyons maintenant un exemple de formulaire qui permet de saisir une date sous la forme « dd/mm/yy » et qui transforme ensuite la chaîne en un objet Date. Pour cela il faut utiliser l’attribut « pattern » et définir le format de date à utiliser. Dans notre cas c’est « dd/MM/yy » : <h:form> <h:inputText id="date" value="#{bean.date}"> <h:outputLabel for="date" value="Date (dd/mm/yy): " /> <f:convertDateTime pattern="dd/MM/yy" /> </h:inputText> <h:commandButton value="valider" /> </h:form> 7.3.2. Conversion standard: Number La balise <f:convertNumber > assure la conversion Number - String. Cette balise possède les attributs suivants : http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 68 JSF - Framework Web 69 / 108 Attribut Type Description currencyCode String Code ISO4217 utilisé pour les conversions monétaires. currencySymbol String Symbole monétaire. locale String Locale integerOnly boolean Si « true » seule la partie entière du nombre sera prise en compte. maxFractionDigits int Nombre maximal de chiffres après la virgule. minFractionDigits int Nombre minimal de chiffres après la virgule. maxIntegerDigits int Nombre maximal de chiffres avant la virgule. minIntegerDigits int Nombre minimal de chiffres avant la virgule. type String Définit ce que représente le nombre. Les valeurs possibles sont : ou La locale pour représenter le nombre. Number (valeur par défaut), currency, percentage. 7.3.3. Convertisseur personnalisé Si les convertisseurs de bases ne vous suffisent pas, il est possible de créer le vôtre. Il suffit d’implémenter « javax.faces.convert.Converter ». Les 2 méthodes à redéfinir sont : /** * @param value la chaîne à transformer en Object */ Object getAsObject(FacesContext context, UIComponent component, String value); /** * @param value L’objet à transformer en String */ String getAsString(FacesContext context, UIComponent component, Object value); Les 2 méthodes ont en paramètre l’instance FacesContext et le composant pour lequel on doit faire la conversion. Avant de pouvoir utiliser ce convertisseur, il faut le déclarer. Cela se fait dans le fichier « facesconfig.xml » à l’aide de la balise <converter>. Exemple de déclaration : <converter> <description>Mon premier convertisseur</description> <converter-id>myConv</converter-id> <converter-class>com.labosun.converter.MyConverter</converter-class> </converter> La balise <description> permet de donner une description au convertisseur. http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 69 JSF - Framework Web 70 / 108 La balise <converter-id> va définir l’identifiant du convertisseur dans les pages JSF. Enfin la balise <converter-class> représente la classe de votre convertisseur. Il existe 2 manières d’utiliser un convertisseur : • grâce à l’attribut « converter » • grâce à la balise <f:converter>. En passant par l’attribut « converter », il suffit de référencer l’identifiant du convertisseur. Exemple : <h:outputText value="#{conv.person}" converter="myConv" /> Avec la balise <f:converter>, l’attribut « converterId » doit référencer l’identifiant du convertisseur. Exemple : <h:outputText value="#{conv.person}"> <f:converter converterId="myConv" /> </h:outputText> 7.3.4. Convertisseurs pour une Liste d’éléments Lors du chapitre sur les Checkbox, boutons Radio, listes et Combo Box, nous avons vu qu’il fallait utiliser un convertisseur pour stocker des objets personnalisés. Pour rappel, les balises concernées sont : • selectManyCheckbox, • selectOneRadio, • selectOneListBox, selectManyListBox, • selectOneMenu Dans cet exemple, nous allons utiliser la balise selectOneRadio qui va permettre de choisir entre plusieurs instances de la classe « Person » suivante : public class Person { public static int IDS = 0; private int id; private String lastName; private String firstName; public Person(int id, String firstName, String lastName) { this.id = id; this.lastName = lastName; this.firstName = firstName; } //Getters et setters pour toutes les propriétés http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 70 JSF - Framework Web 71 / 108 ... public String toString() { if (firstName.length() > 1) { return this.firstName + " " + this.lastName; } else { return this.lastName; } } } Cette classe définie simplement une personne qui possède un identifiant unique, un nom et un prénom. Nous allons maintenant définir le bean qui va définir la liste des SelectItem. Nous allons commencer par voir ce qui se passe sans convertisseur de données. package com.labosun; public class PersonConv { /** * Liste qui simule une source de données */ public static final List<Person> PERSONS_LIST = new ArrayList<Person>(); static { PERSONS_LIST.add(new Person(1, "Jo", "Johnson")); PERSONS_LIST.add(new Person(2, "Raymond", "Schmitt")); PERSONS_LIST.add(new Person(3, "Bob", "LeNain")); } /** * Liste de SelectItem pour représenter le contenu d'une balise select.... */ private List<SelectItem> allPersons; /** * Personne actuellement sélectionné pour une balise de type selectOne.. */ private Person person; public PersonConv() { allPersons = new ArrayList<SelectItem>(); for (int i = 0; i < PERSONS_LIST.size() - 1; i++) { Person p = PERSONS_LIST.get(i); this.allPersons.add(new SelectItem(p, p.toString())); } Person p = new Person(3, "Bob", "LeNain"); this.allPersons.add(new SelectItem(p, p.toString())); } //Getters et setters pour toutes les propriétés ... } Le constructeur se charge de créer une liste de SelectItem (allPersons) à partir de notre liste de personnes (PERSON_LIST). Mais la dernière instance de personne n’est pas reprise de la liste, ceci est volontaire, vous allez comprendre dans la suite pourquoi. http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 71 JSF - Framework Web 72 / 108 Voici maintenant notre page JSF. Elle contient autant de boutons radios qu’il y a de personnes dans la liste et elle affiche l’élément actuellement sélectionné. <hform> <h:selectOneRadio id="persRadio1" value="#{personList.person}"> <f:selectItems value="#{personList.allPersons}" /> </h:selectOneRadio> <h:message for="persRadio1" /> <br /> Résultat: <h:outputText value="#{personList.person}" /> <br /><br /> <h:commandButton value="Valider" /> </hf:form> Ce qui donne : Lorsqu’on tente de valider, on obtient le message d’erreur suivant : Si on regarde le code HTML, on peut voir la chose suivante : Jo Johnson <input type="radio" value="Jo Johnson" /> Raymond Schmitt <input type="radio" value="Raymond Schmitt" /> Bob LeNain <input type="radio" value="Bob LeNain" /> On remarque ici que la valeur des boutons radio (attribut value dans le code HTML) utilise la méthode toString() de la classe Person. Cela veut dire que lorsque la page JSF est traduite en HTML, les objets de types Person sont traduits en String grâce à la méthode toString(). Mais lorsque l’arbre des composants JSF doit être reconstruit, la Servlet JSF ne sait pas comment réinstancier l’objet Person à partir de cette String. Il faut donc créer un convertisseur qui va se charger de cette traduction. Pour créer le convertisseur, nous allons nous baser sur l’identifiant de la personne. package com.labosun; public class PersonConv implements Converter { //Code précédent ... /** * @param value l'identifiant de la personne * @return L'objet Person qui correspond à l'identifiant contenu dans value */ public Object getAsObject(FacesContext ctx, UIComponent component, String value) { http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 72 JSF - Framework Web 73 / 108 int id = Integer.valueOf(value); for (Person p : PERSONS_LIST) { if (p.getId() == id) { return p; } } return null; } /** * @param value La personne à transformer en String * @return L'identifiant de la personne sous forme de String */ public String getAsString(FacesContext ctx, UIComponent component, Object value) { if (value == null || !(value instanceof Person)) { return ""; } return "" + ((Person) value).getId(); } } Il faut déclarer le convertisseur : <converter> <converter-id>persConv</converter-id> <converter-class>com.labosun.PersonConv</converter-class> </converter> Il reste maintenant à référencer le convertisseur dans notre balise selectOneRadio : <h:selectOneRadio id="persRadio1" value="#{personList.person}" converter="persConv"> <f:selectItems value="#{personList.allPersons}" /> </h:selectOneRadio> Tout fonctionne très bien pour les 2 premières instances, mais lorsqu’on sélectionne le 3ème bouton, on obtient le message d’erreur suivant : Si on regarde le code HTML, on peut voir : Jo Johnson <input type="radio" value="1" /> Raymond Schmitt <input type="radio" value="2" /> Bob LeNain <input type="radio" value="3" /> La valeur des boutons radio utilise bien l’identifiant des personnes pour les 3 boutons. Le message d’erreur spécifique au 3ème bouton est là car JSF va vérifier si l’objet que retourne le convertisseur appartient bien à la liste des items. Cette vérification se fait à l’aide des méthodes hashCode() et equals(). Dans notre cas, notre convertisseur retourne des instances provenant de l’objet « PERSONS_LIST », et ensuite JSF va vérifier si l’instance retournée appartient bien à la liste « allPersons ». Hors, http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 73 JSF - Framework Web 74 / 108 souvenez vous que seules les 2 premières instances de la liste « allPersons » proviennent de la liste « PERSONS_LIST ». Comme la classe Person ne redéfinit pas les méthodes hashCode() et equals(), ce sont celles de la classe Object qui sont utilisées. C’est-à-dire que l’égalité est basée uniquement sur l’égalité des références (==). Ceci fonctionne donc seulement pour les 2 premières instances. Pour que ça fonctionne dans tous les cas, il suffit d’implémenter ces 2 méthodes dans la classe Person : public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } return this.getId() == ((Person) o).getId(); } public int hashCode() { return this.getId(); } Voilà maintenant tout fonctionne correctement. En résumé il faut : • Créer un convertisseur (Implémenter Converter). • Définir le convertisseur dans « faces-config.xml ». • Référencer le convertisseur dans la balise select.... (attribut converter) • Implémenter les méthodes hashCode() et equals() Cet exemple est facilement adaptable pour une balise de type selectMany... 7.4. Validation JSF permet de valider toutes les données saisies par l’utilisateur. La validation n’est possible que sur les composants qui implémentent EditableValueHolder. C’est-à-dire les champs textes, les checkbox, les radio buttons, les listes et les combo box. La validation est effectuée pendant la phase « Validation des entrées utilisateurs ». Attention, pendant celle-ci, tous les éléments ne sont pas encore à jour (les javabeans sont mis à jour seulement pendant la phase « Mise à jour du modèle »). S’il y a plusieurs validations, l’ordre de traitements des validations est l’ordre de déclaration des balises. http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 74 JSF - Framework Web 75 / 108 Remarque : Si le composant qui déclare la validation a l’attribut « immediate » à true, alors la validation est traitée pendant la phase « Application des paramètres de la requête ». Cela permet de forcer une validation à se faire avant toutes les autres. Par exemple, une personne désire s’enregistrer sur un site. Elle doit saisir le pays et la ville dans laquelle elle habite. La validation du pays peut se faire en immediate pour être sûr que la validation de la ville tienne compte du pays. Pour forcer l’utilisateur à saisir une valeur, vous pouvez mettre l’attribut « required » à true. Exemple : <h:inputText required="true" /> Pour effectuer un contrôle plus précis sur les données, il faut utiliser un validateur. JSF fournit 3 validateurs standards. Ils sont utilisables grâce aux balises : • <f:validateDoubleRange>, permet de tester si un nombre à virgule se trouve dans une borne. • <f:validateLength>, permet de tester la longueur d’une chaîne. • <f:validateLongRange>, permet de tester si un nombre entier se trouve dans une borne. Toutes ces balises possèdent les attributs « minimum » et « maximum » qui définissent les bornes dans lesquelles doit se trouver une valeur pour être valide. Exemple d’utilisation : <h:form> ... <h:inputText id="name"> <h:outputLabel for="name" value="Name:" /> <f:validateLength minimum="3" maximum="10" /> </h:inputText> <%-- Affichage du message d'erreur de la validation %--> <h:message for="name" /> ... </h:form> Ici le formulaire tout entier ne sera pas validé si la chaîne rentrée pour ce champ texte n’a pas une longueur comprise entre 3 et 10. Les erreurs de validations sont affichées par la balise <h:message>. 7.4.1. Validateur personnalisé Il est possible de créer son propre validateur. Il existe 2 manières de la faire, soit en utilisant une méthode, soit en implémentant l’interface « javax.faces.validator.Validator ». http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 75 JSF - Framework Web 7.4.1.1. 76 / 108 Méthode générique N'importe quel JavaBean peut déclarer la méthode, mais il doit être référencé en tant que JavaBean géré par le conteneur. La méthode qui gère la validation doit avoir le prototype suivant : public void validate(javax.faces.context.FacesContext context, javax.faces.component.UIComponent component, Object value) { //Gestion de l'événement } S’il y a une erreur de validation, la méthode doit lancer une « javax.faces.validator.ValidatorException ». Le constructeur prend en paramètre un objet de type « javax.faces.application.FacesMessage », c’est-à-dire un message qui est destiné à l’utilisateur. Ce message sera ensuite affiché grâce à la balise <h:message>. La méthode peut ensuite être référencée grâce à l’attribut « validator ». <h:inputText validator="#{nomDuBean.methodName }" /> 7.4.1.2. Interface Il faut implémenter l’interface « javax.faces.validator.Validator ». La seule méthode à redéfinir est : public void validate(FacesContext context, UIComponent component, Object value); Pour utiliser ce validateur, il faut le déclarer dans le fichier « faces-config.xml » à l’aide de la balise <validator >. Exemple : <validator> <!-- Indentifiant du validateur dans les pages JSF --> <validator-id>emailValidator</validator-id> <!-- Classe du validateur --> <validator-class>com.labosun.validation.EmailValidator</validator-class> </validator> Le validateur peut ensuite s’utiliser grâce à la balise <f:validator>, l’attribut « validatorId » doit référencer l’identifiant du validateur. Exemple : <h:inputText> <f:validator validatorId="emailValidator" /> </h:inputText> 7.4.2. Exemple : Validateur d’email Pour illustrer le fonctionnement d’une validation, nous allons créer un validateur d’email très basique. Nous vérifierons juste la présence du caractère « @ ». public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException { if ((context == null) || (component == null)) { throw new NullPointerException(); } if (value != null) { String field = (String) value; if (field.contains("@") == false) { http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 76 JSF - Framework Web 77 / 108 FacesMessage fm = new FacesMessage("L'email n'est pas valide"); throw new ValidatorException(fm); } } } Nous allons maintenant déclarer notre bean dans le fichier « faces-config.xml ». <managed-bean> <managed-bean-name>beanMail</managed-bean-name> <managed-bean-class> com.labosun.EmailValidator</managed-bean-class> <managed-bean-scope>application</managed-bean-scope> </managed-bean> Il ne reste plus qu’à écrire le code de la page et à référencer la méthode de notre bean grâce à l’attribut validator. <h:form> <h:inputText id="emailMethod" validator="#{beanMail.checkEmail}"> <h:outputLabel for="emailMethod" value="Email:" /> </h:inputText> <h:message for="emailMethod" /> <br /> <h:commandButton value="Vérifier l'email" /> </h:form> Quand on rentre un texte qui ne contient pas de « @ », la balise <h:message> affiche le message « Un email doit contenir un @ ». http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 77 JSF - Framework Web 78 / 108 8. Concepts avancés 8.1. Internationalisation L’internationalisation des pages JSF se fait à l’aide de fichiers de ressources. Un fichier de ressources est un ensemble de clefs valeurs. Voir le tutorial de Sun sur les fichiers de ressources si vous ne connaissez pas le concept : http://java.sun.com/docs/books/tutorial/i18n/index.html. Pour utiliser un fichier de ressource depuis une page JSF, il faut utiliser la balise <f:loadBundle>. Exemple: <f:loadBundle basename="nomFichier" var="variable"/> • L’attribut « basename » référence le nom du fichier de propriétés que l’on veut utiliser. Attention, si le fichier se trouve dans un package, il faut préfixer le nom du fichier par le nom du package. • L’attribut « var » sera utilisé dans le reste de la page pour référencer ce fichier de propriétés. Dans la suite de la page, on peut alors utiliser les chaînes définies dans le fichier de propriétés de la manière suivante : <h:outputText value="#{variable.nomChaine}"/> Exemple : On a le fichier de propriétés « SiteMessages.properties » qui se trouve dans le package com.labosun. Ce fichier contient l'entrée suivante: message = coucou Pour l'utiliser dans une page, il suffit d'utiliser le code suivant: <f:loadBundle basename="com.labosun.SiteMessages" var="msg"/> <f:view> <h:outputText value="#{msg.message}"/> </f:view> Dans ce cas, la locale utilisée sera la locale pour l'utilisateur en cours. Si vous voulez changer la locale par défaut, il faut utiliser le code suivant dans le fichier « facesconfig.xml » : <application> <locale-config> <default-locale>en</default-locale> </locale-config> </application> Le chapitre « 8.1.3 Exemple : Bouton de changement de langue » explique comment changer la locale pour un utilisateur. http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 78 JSF - Framework Web 79 / 108 8.1.1. Internationalisation dynamique Il est parfois nécessaire d’accéder à un fichier de ressource depuis un JavaBean. Pour cela il faut utiliser la méthode getBundle de la classe ResourceBundle. Le premier paramètre de cette méthode est le nom du fichier de ressource à utiliser et le deuxième est la locale que l'on veut utiliser. FacesContext context = FacesContext.getCurrentInstance(); String basename = "conf.SiteMessages"; String key = "dynamicMessage"; String message; try { ResourceBundle bundle = ResourceBundle.getBundle( basename, context.getViewRoot().getLocale()); } catch (Exception e) { //Gestion de l'erreur } //Utilisation d'un message du fichier de ressource message = bundle.getString(key); 8.1.2. Encodage Normalement la notion d’encodage a déjà été vue dans le cours JSP, mais pour rappel, il est possible de changer l’encodage des pages grâce à la directive de page : <%@ page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8" %> Par défaut c’est la valeur « ISO-8859-1 » qui est utilisée. La liste des encoding disponibles se trouve sur la page : http://java.sun.com/j2se/1.4.2/docs/guide/intl/encoding.doc.html 8.1.3. Exemple : Bouton de changement de langue Dans cet exemple, nous allons voir comment changer la langue du site quand l’utilisateur clique sur un lien. Commençons par créer la page Web. Celle-ci affiche la Locale actuelle et 4 liens qui permettent de changer la langue du site. <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <body> <f:view> <%--On charge le fichier de ressource--%> <f:loadBundle basename="com.labosun.i18n.ChangeLocaleRB" var="change" /> <%-- Affichage de la locale actuelle--%> <h:outputText value="#{change.currentLocale} #{changeBean.changeLocale}" /> <br /> <h:form> <%-- Liens pour changer de langue--%> <h:commandLink id="FR" value="#{change.french}" actionListener="#{changeBean.changeLocale}" /><br /> <h:commandLink id="US" value="#{change.english}" actionListener="#{changeBean.changeLocale}" /><br /> http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 79 JSF - Framework Web 80 / 108 <h:commandLink id="CA" value="#{change.canadian}" actionListener="#{changeBean.changeLocale}" /><br /> <h:commandLink id="DE" value="#{change.deutsch}" actionListener="#{changeBean.changeLocale}" /><br /> </h:form> </f:view> </body> </html> Comme vous avez pu le remarquer, cette page utilise un fichier de ressource. Voici le contenu de 2 fichiers de ressources (anglais et français): Fichier « ChangeLocaleRB_fr.properties »: currentLocale=La Locale actuelle est french=Français english=Anglais canadian=Canadien deutsch=Allemand Fichier « ChangeLocaleRB_en.properties »: currentLocale=The current Locale is french=French english=English canadian=Canadian deutsch=Deutsch Il faut maintenant écrire le bean qui va prendre en charge le changement de langue. package com.labosun.i18n; import import import import javax.faces.component.UIComponent; javax.faces.context.FacesContext; javax.faces.event.ActionEvent; java.util.Locale; public class ChangeBean { /** * Contient la Locale courante */ private Locale currentLocale = FacesContext.getCurrentInstance(). getViewRoot().getLocale(); public Locale getCurrentLocale() { return currentLocale; } /** * On change la locale pour la vue en cours. * @param newLocale La nouvelle Locale */ public void setCurrentLocale(Locale newLocale) { FacesContext.getCurrentInstance().getViewRoot().setLocale(newLocale); this.currentLocale = newLocale; } public void changeLocale(ActionEvent ae) { String id = ae.getComponent().getId(); Locale newLocale = null; //on récupère toutes les Locale disponible http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 80 JSF - Framework Web 81 / 108 Locale[] locales = Locale.getAvailableLocales(); for (int i = 0; newLocale == null && i < locales.length; i++) { if(locales[i].getCountry().equals(id)) { newLocale = locales[i]; } } if (newLocale == null) { newLocale = Locale.getDefault(); } setCurrentLocale(newLocale); } } Enfin il faut déclarer notre JavaBean dans le fichier « faces-config.xml ». <faces-config> <managed-bean> <managed-bean-name>changeBean</managed-bean-name> <managed-bean-class>com.labosun.i18n.ChangeBean</managed-bean-class> <managed-bean-scope>application</managed-bean-scope> </managed-bean> </faces-config> 8.2. La gestion des messages Comme vu lors du chapitre « 5.15 Message d’erreur », toutes les erreurs rencontrées sont remontés à l’utilisateur via un système de message. De manière plus générale il est possible depuis n’importe quel type de JavaBean (actionListener, convertisseur, validation, …) d’envoyer un message qui va être affichée dans une page. Un message JSF est représenté par la classe « javax.faces.application.FacesMessage ». Chaque message possède un résumé de l’erreur (propriété « summary »), un détail de l’erreur (propriété detail), et l’importance de l’erreur (propriété « severity »). Les messages sont enregistrés dans l’application grâce à la méthode « addMessage(String clientId, FacesMessage message) » de la classe FacesContext. Le paramètre « cliendId » permet de lier le message à un composant, s’il est null, alors il s’agit d’un message global. Pour avoir un identifiant valide, le plus simple est de récupérer directement l’identifiant du composant grâce à la méthode « UIComponent.getId() ». Voyons maintenant un exemple de création de message depuis un JavaBean. Exemple : package com.labosun.message; import import import import javax.faces.application.FacesMessage; javax.faces.component.UIInput; javax.faces.context.FacesContext; javax.faces.event.ActionEvent; public class MessageBean { private UIInput nameInput; public UIInput getNameInput() { return nameInput; } http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 81 JSF - Framework Web 82 / 108 public void setNameInput(UIInput nameInput) { this.nameInput = nameInput; } public void check(ActionEvent ae) { FacesContext context = FacesContext.getCurrentInstance(); //Retrieve component id String nameId = nameInput.getClientId(context); FacesMessage f = new FacesMessage(FacesMessage.SEVERITY_WARN, "Global message summary", "message from MessageBean"); context.addMessage(null, f); FacesMessage f2 = new FacesMessage(FacesMessage.SEVERITY_INFO, "Specific message summmary", "message from MessageBean and specific to component 'name'"); context.addMessage(nameId, f2); } La propriété « nameInput » va être liée à l’instance d’un composant pour pouvoir récupérer son identifiant exact. La méthode check sert d’ActionListener. C’est elle qui génère les 2 messages « f » et « f2 ». Le premier message est global puisqu’on n’utilise pas d’identifiant lors de l’ajout « context ». Le deuxième message est spécifique au composant nameInput. Pour lier la propriété « nameInput » avec le composant de type champ texte de la page JSF, il faut utiliser l’attribut « binding ». <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <head> <link href="style.css" rel="stylesheet" type="text/css" /> </head> <body> <f:view> <h:messages globalOnly="true" showDetail="false" showSummary="true" styleClass="errorTable" errorClass="error" infoClass="info" fatalClass="fatal" warnClass="warn" /> <h:form> <h:inputText id="name" size="10" binding="#{messageBean.nameInput}"> <h:outputLabel for="name" value="Name:" /> </h:inputText> <h:message for="name" styleClass="errorTable" errorClass="error" infoClass="info" fatalClass="fatal" warnClass="warn" /> <h:commandButton value="Valider" actionListener="#{messageBean.check}"/> <br /> <h3>List</h3> <h:messages showDetail="true" showSummary="true" styleClass="errorTable" errorClass="error" infoClass="info" fatalClass="fatal" warnClass="warn" /> http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 82 JSF - Framework Web 83 / 108 <h3>Table</h3> <h:messages showDetail="true" showSummary="false" layout="table" styleClass="errorTable" errorClass="error" infoClass="info" fatalClass="fatal" warnClass="warn" /> </h:form> </f:view> </body> </html> Dernière étape, il faut enregistrer le JavaBean dans le fichier « faces-config.xml ». <managed-bean> <managed-bean-name>messageBean</managed-bean-name> <managed-bean-class>com.labosun.message.MessageBean</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> </managed-bean> 8.2.1. Refaire les messages Les messages d’erreurs par défaut ne correspondent pas toujours à votre application. Heureusement il est possible de les modifier. En JSF 1.2, tous les composants qui héritent de UIInput permettent de personnaliser les messages très facilement grâce à 3 attributs : • « converterMessage », référence le message à afficher quand la valeur du composant ne peut pas être convertie par le convertisseur référencé par ce composant. • « requiredMessage », référence le message à afficher quand aucune valeur n'a été entrée • « validatorMessage », référence le message à utiliser quand la valeur saisie ne peut pas être validée par le validateur référencé par ce composant. Pour tous les autres messages et pour JSF 1.1, les messages d'erreurs par défaut se trouvent dans le fichier de propriétés « Message_fr.properties » qui se trouvent dans le package « java.faces » du jar « jsf-impl.jar ». Il est possible de redéfinir ces messages en créant un nouveau fichier de propriétés qui va redéfinir les clefs utilisées. Ce nouveau fichier peut être placé n’importe où dans votre application. On va ensuite le référencer pour qu’il remplace le fichier par défaut. Par exemple, nous allons créer le fichier « MyDefaultMessages_fr.properties » dans lequel nous allons copier les chaînes du fichier original: « Message_fr.properties ». javax.faces.component.UIInput.REQUIRED=Erreur de validation : Vous devez indiquer une valeur. javax.faces.validator.LengthValidator.MAXIMUM=Erreur de validation : la valeur est sup\u00e9rieure \u00e0 la valeur maximale autoris\u00e9e, ''{0}'' Vous remarquerez que tous les caractères spéciaux sont unicode. Dans ces messages, la syntaxe « {0} » permet d’insérer un paramètre. Ce mécanisme utilise la classe MessageFormat. Nous allons maintenant mettre les messages suivants : javax.faces.component.UIInput.REQUIRED= Message personnalisé: Ce champ est obligatoire. javax.faces.validator.LengthValidator.MAXIMUM=Message personnalisé: La valeur ''{0}'' est sup\u00e9rieure \u00e0 la valeur maximale pour ce champ http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 83 JSF - Framework Web 84 / 108 La dernière étape pour que ce fichier soit utilisé à la place du fichier par défaut est de référencer votre fichier dans le fichier de configuration de JSF. Cela se fait à l'aide de la balise <application>. <faces-config> <application> <locale-config> <default-locale>fr</default-locale> </locale-config> <message-bundle>MyDefaultMessages</message-bundle> </application> </faces-config> 8.3. Intégration JSF et application JEE La nouvelle spécification JEE 5 apporte des annotations qui peuvent être utilisé pour interagir avec les couches plus basses de votre application grâce au principe de l’injection. C’est-à-dire que l’on va déclarer une variable qui va être instanciée par le conteneur. Il faut un serveur conforme à JEE 5 pour utiliser ces annotations. 8.3.1. Injection de ressource L’injection de ressource permet par exemple de récupérer une instance du contexte JNDI courant. Elle s’utilise grâce à l’annotation « @Resource ». Exemple : @Resource private SessionContext ctx; Lors du déploiement de l’application, la variable « ctx » sera initialisée. 8.3.2. Injection d’EJB L’injection d’EJB permet de récupérer une instance de l’interface Local ou Remote. Elle s’utilise grâce à l’annotation « @EJB ». Exemple : @EJB private Store store; Il est aussi possible d’utiliser l’injection de ressource pour récupérer une instance sur le contexte JNDI courant et ensuite y rechercher l’EJB. Exemple : @Resource private SessionContext ctx; Store store = (Store)ctx.lookup("Store"); http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 84 JSF - Framework Web 85 / 108 8.3.3. Injection de WebService Il est possible de récupérer un WebService qui hérite de « javax.xml.ws.Service ». L’injection s’utilise grâce à l’annotation « @WebServiceRef ». Exemple : @WebServiceRef public StockQuoteService stockQuoteService; 8.3.4. Injection d’unité de persistance Il est possible de récupérer une instance d’un EntityManager. Comme en EJB3, l’injection se fait grâce à l’annotation « @PersistenceContext ». Le champ « unitName » permet de renseigner le nom de l’unité de persistance à utiliser. Pour être valide, ce nom doit être déclaré dans le fichier « persistence.xml ». Exemple : @PersistenceContext(unitName="InventoryManagement") EntityManager em; http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 85 JSF - Framework Web 86 / 108 9. Facelets 9.1. Introduction JSF a été conçu pour fonctionner avec JSP, mais il est évident qu’il existe de nombreux problèmes quand on mélange les 2. Rien que le langage unified EL doit être utilisé de 2 manières différentes en fonction du contexte JSP ou JSF (syntaxe ${} et #{}). Facelets vise à créer un jeu de balise qui s’accorde bien avec JSF et apporte également une nouvelle manière d’écrire du code JSF. Il n’apporte pas de nouveaux mécanismes internes à JSF, mais se contente de faciliter l’écriture du code en permettant notamment de créer des templates de code. Facelets fonctionne avec les implémentations de JSF 1.1 et 1.2 de Sun et Apache MyFaces. Facelets fournit également les améliorations suivantes : • La possibilité de créer des pages en plusieurs fichiers. • Un affichage plus précis des erreurs. • Création rapide de balises personnalisées • Configuration simple • Permet d’utiliser les balises JSF sous forme de balise HTML standard (attribut jsfc, voir le chapitre « 9.3.2 JSFC ») Pour bien comprendre ce qu’apporte Facelets il faut revenir au cycle de vie d’une page Web. JSF a été conçu pour être modulable, ce qui veut dire qu’il est possible de changer certains composants dans le fonctionnement de JSF. Facelets se charge de remplacer le composant qui se charge de la vue, c’est-àdire de la phase « Restauration de la vue » et « Rendu de la réponse » dans le cycle de vie. Les autres phases du cycle de vie restent inchangées. Voici un rappel du cycle de vie, les parties en jaune sont les parties gérées par Facelets. http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 86 JSF - Framework Web 87 / 108 Grâce au contrôle de ces 2 phases, toute la syntaxe des fichiers peut être modifiée et entièrement repensée par Facelets. 9.2. Configuration Dans ce chapitre, nous allons voir comment installer et configurer Facelets pour l’utiliser dans un projet. 9.2.1. Installation Tout d’abord vous devez télécharger la dernière implémentation de Facelets à l’adresse : https://facelets.dev.java.net/servlets/ProjectDocumentList?folderID=3635&expandFolder=3635&folde rID=3635 Ce cours est basé sur la version « 1.1.11 ». Il faut ensuite décompresser l’archive et récupérer le fichier « jsf-facelets.jar ». Dans le cas où vous n’utiliseriez pas un serveur JEE 5, il faut aussi prendre les fichiers « el-api.jar » et « el-ri.jar » qui se trouvent dans le répertoire « lib ». 9.2.2. Structure d’un projet Facelets respecte la même structure qu’une application JSF standard. La seule différence va être l’extension des pages Web, qui est « .xhtml ». Cette extension n’est pas obligatoire, mais la syntaxe de Facelets respecte le format XHTML, il est donc plus logique d’utiliser cette extension. La structure d’un fichier War reste la même que pour JSF. Voici ce que cela donne : WEB-INF/classes Contient toutes les classes java (les JavaBeans entre autre) WEB-INF/lib Toutes les librairies (jar) nécessaires au projet. La librairie de Facelets : « jsf-facelets.jar » Eventuellement : « el-api.jar » et « el-ri.jar » WEB-INF/faces-config.xml Fichier de configuration WEB-INF/web.xml *.xhtml A la racine, il faut placer toutes les pages « .xhtml ». Ce sont les pages Facelets. 9.2.3. Configuration Nous allons maintenant voir les modifications à apporter au fichier « faces-config.xml » et « web.xml » pour pouvoir utiliser Facelets. http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 87 JSF - Framework Web 88 / 108 Tout d’abord nous allons définir l’extension « .xhtml » comme extension des pages. Comme vu lors du chapitre « 6.3 Paramètres de contexte », il faut utiliser le paramètre de contexte « javax.faces.DEFAULT_SUFFIX ». <web-app> <!-- Extension .xhtml pour les pages JSF--> <context-param> <param-name>javax.faces.DEFAULT_SUFFIX</param-name> <param-value>.xhtml</param-value> </context-param> <!-- Paramètre spécifique à Facelets. Utile pour le développement --> <context-param> <param-name>facelets.DEVELOPMENT</param-name> <param-value>true</param-value> </context-param> </web-app> Dans les exemples qui vont suivre, c’est le mapping en mode suffixe « .jsf » qui est utilisé. Ceci permet d’appeler une page en remplaçant l’extension « .xhtml » par « .jsf ». Pour rappel, voici comment le déclarer : <web-app> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.jsf</url-pattern> </servlet-mapping> </web-app> Il faut maintenant que la Servlet JSF passe à la main à Facelets pour la gestion de la vue. Cela se fait dans le fichier « faces-config.xml » : <faces-config> <application> <view-handler>com.sun.facelets.FaceletViewHandler</view-handler> </application> </faces-config> Grâce à cette configuration, la gestion de la vue (création de l’arbre des composants JSF, rendu de la réponse) sera gérée par Facelets et non plus par l’implémentation de JSF. 9.3. Concepts de base 9.3.1. Structure d’une page La structure des pages respecte le format XHTML. Voici un premier exemple de page Facelets. Hello.xhtml : http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 88 JSF - Framework Web 89 / 108 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <body> <h:outputText value="Hello" /> #{param.f} </body> </html> La première différence par rapport à une page JSF classique est que la déclaration des taglibs est différente. Ici on déclare toutes les librairies sous forme de namespaces depuis la balise <html>. L’expression « xmlns:ui=http://java.sun.com/jsf/facelets » permet d’utiliser toutes les balises de Facelets. Tandis que les 2 suivantes permettent d’utiliser les librairies de JSF. Il n’y a pas de balise <f:view>. Enfin il est possible d’utiliser une expression EL directement dans du code HTML. Par exemple le code « #{param.f} » affiche directement la valeur du paramètre « f ». Si on exécute cette page en passant le paramètre « f=Facelets » dans l’url, alors On obtient le texte : Hello Facelets Par exemple, si « JSFCoursFacelets » est le contexte du projet et « Hello.jsf » est le nom de la page. Il faut utiliser l’url : http://localhost:8080/JSFCoursFacelets/Hello.jsf?f=Facelets. 9.3.2. JSFC Un des problèmes qui se pose avec les balises JSF est que les éditeurs ne savent pas tous rendre une page de manière graphique. Par contre la plupart des éditeurs de code savent afficher une page HTML. Une des possibilités qu’offre Facelets pour pouvoir afficher le résultat d’une page sous forme HTML et sans qu’elle soit exécutée sur un serveur. Ceci se fait grâce à l’attribut « jsfc ». Voyons un exemple : <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Formulaire avec JSFC</title> </head> <body> <form jsfc="h:form"> <input id="name" type="text" jsfc="h:inputText" value="#{bean.name}" required="true"> <label for="name">Nom :</label> http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 89 JSF - Framework Web 90 / 108 </input> <h:message for="name" /> <br /> <button type="submit" jsfc="h:commandButton" value="Valider" /> </form> </body> </html> Prenons la balise <form jsfc="h:form">, Facelets va se charger de l’interpréter en tant que balise <h:form>, mais du point du vue d’un éditeur de code il s’agit d’une simple balise <form>. L’attribut « jsfc » sert à définir le type réel de la balise. L’avantage ici de pouvoir travailler avec des balises HTML standard ce qui permet d’afficher la page de manière graphique dans un simple éditeur. Les attributs spécifiques à une balise JSF doivent être rajouté à la balise HTML. Un éditeur HTML va très certainement indiquer que l’attribut n’existe pas pour cette balise, mais cela va fonctionner correctement pour Facelets. C’est le cas de l’attribut « required » pour la balise <input>. Pour les balises JSF qui n’ont pas d’équivalent en HTML comme la balise <h:message> par exemple, il faut l’inclure directement dans le code. On obtient ainsi un mélange de balises HTML et JSF, sans oublier qu’on peut toujours rajouter des expressions EL directement dans le code HTML. 9.3.3. Template Un template est du code Facelets qui peut être réutilisé. C’est un modèle qu’on peut appliquer à volonté dans un site. C’est un petit peu l’équivalent d’une balise, sauf qu’un template s’écrit de manière très simple et est plus facilement modulable. Une page qui utilise un template est appelée « page appelante ». Nous verrons par la suite qu’il existe plusieurs manières d’utiliser un template. Cette partie du cours se concentre uniquement sur la déclaration basique d’un template. Tout fichier Facelets peut servir de template. Par défaut, c’est l’ensemble d’une page qui définit le template. Template1.xhtml : <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title> </title> </head> <body> </body> </html> http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 90 JSF - Framework Web 91 / 108 Ici la page entière est considérée comme un template. Pour n’utiliser qu’une portion de code comme template, il faut utiliser la balise <ui:composition>. Template2.xhtml : <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title> </title> </head> <body> <ui:composition> Partie de template </ui:composition> </body> </html> Ici seul le corps de la balise <ui:composition> sert de template, la partie grisée est ignorée. Une des manière d’utiliser un template est d’utiliser à nouveau la balise <ui:composition> à la différence que son attribut « template » doit préciser le nom du fichier de template à utiliser. TemplateUtilisation.xhtml : <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title> </title> </head> <body> <ui:composition template="Template1.xhtml"> </ui:composition> </body> </html> Le résultat de cet exemple va être que le contenu de la page « template1.xhtml » sera affiché chez le client. Cet exemple est plutôt limité vu qu’il est équivalent à une inclusion de page. Nous verrons dans le prochain chapitre comment créer et utiliser des templates modifiables. Dans cette exemple, toutes les balises de la page qui utilisent le template sont ignorées. Ce comportement est spécifique à la balise <ui:composition>, le chapitre « 9.5 Balises Facelets » présente d’autres balises pour utiliser des templates. http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 91 JSF - Framework Web 9.4. 92 / 108 Templating de vue Maintenant que nous avons la syntaxe de base de Facelets et le concept de template, nous allons pouvoir voir en pratique ce que cela permet de faire. Un des problèmes le plus courant lors de la création d’un site Web est que chaque page doit redéfinir l’ensemble de la mise en page du site. C’est-à-dire redéfinir le titre, le menu, le pied de page, ... . Or ces éléments sont souvent les mêmes pour toutes les pages du site. Il est plus pratique de ne les déclarer qu’une seule fois et qu’ils soient ensuite intégrés directement aux autres pages. Facelets permet de faire cela grâce à son système de template. Voyons avec un premier exemple comme ça fonctionne. Tout d’abord il faut écrire la page de template. C’est elle qui décrit la structure du site et les éléments que les autres pages peuvent modifier. Dans notre exemple, nous allons créer un site qui contient un en-tête, un menu, un corps et un pied de page. MainTemplate.xhtml : <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title> <ui:insert name="Title">Default title</ui:insert> </title> </head> <body> <div style="background: cornflowerblue; position: absolute; left: 0%; right: 0%; bottom: 95%; top: 0%"> <ui:insert name="Header"> <ui:include src="Header.xhtml" /> </ui:insert> </div> <div style="background: darksalmon; position: absolute; left: 0%; right: 90%; bottom: 5%; top: 5%;"> <ui:insert name="Menu"> <a href="">Default Menu 1</a><br /> <a href="">Default Menu 2</a><br /> <a href="">Default Menu 3</a><br /> </ui:insert> </div> <div style="position: absolute; left: 10%; right: 0%; bottom: 5%; top: 5%;"> <ui:insert name="Body"> Default body </ui:insert> </div> http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 92 JSF - Framework Web 93 / 108 <div style="background: lawngreen; position: absolute; left: 0%; right: 0%; bottom: 0%; top: 95%"> <ui:insert name="Footer"> Default Footer </ui:insert> </div> </body> </html> Il y a 2 balises spécifiques à Facelets dans cette page de template, <ui:insert> et <ui:include>. Tout d’abord les balises <ui:insert>, ce sont elles qui permettent de définir les parties modifiables du template. Chaque balise utilise un nom qui sert à identifier une partie. Dans le cas de la balise : <ui:insert name="Footer"> Default Footer </ui:insert> Elle définit toute la partie du pied de page qui est modifiable. Le texte « Defautl Footer » est le texte qui s’affiche par défaut, lorsqu’une autre page va utiliser ce template sans modifier le pied de page. Pour la balise : <ui:insert name="Header"> <ui:include src="Header.xhtml" /> </ui:insert> Elle ne définit pas directement l’en-tête par défaut, mais importe un fichier qui va contenir le texte par défaut. L’import se fait avec la balise <ui:include>. HeaderTemplate.xhtml : <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title></title> </head> <body> <ui:composition> Default Header </ui:composition> </body> </html> Cette page contient le code par défaut de l’en-tête. La balise <ui:composition> sert à définir un bloc de code qui peut être réutilisé dans d’autres pages. En fait, tout le code qui se trouve autour de la balise http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 93 JSF - Framework Web 94 / 108 <ui:composition> est ignoré, c’est-à-dire qu’il ne va jamais apparaître dans la page HTML que reçoit le client. Ce comportement est dû à la balise <ui:include> qui ignore le reste de la page dès qu’elle trouve une balise <ui:composition>. Nous verrons plus en détail le principe d’inclusion dans le chapitre « 9.5.5 Include ». Notre template est maintenant prêt, nous pouvons créer la page qui va utiliser ce template. PageMain.xhtml : <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Page principale</title> </head> <body> <ui:composition template="MainTemplate.xhtml"> <!-Une balise define doit correspondre à une balise <ui:insert> du template <ui:define name="Title">Page principale</ui:define> --> <ui:define name="Footer">Pied de page</ui:define> <ui:define name="Body">Corps de la page main redéfini</ui:define> </ui:composition> </body> </html> La balise <ui:composition> : <ui:composition template="MainTemplate.xhtml"> ... </ui:composition> utilise l’attribut « template ». Dans ce cas, elle ne sert pas à déclarer un template (comme dans HeaderTemplate.xhtml) mais à préciser qu’on veut utiliser un bloc de code existant. Dans notre cas, on commence par prendre tout le code de la page de l’attribut « template », « MainTemplate.xhtml ». Ensuite on remplace les parties modifiables (corps des balises <ui:insert>) par le contenu des balises <ui:define>. Ici on ne modifie que les parties « Title », « Footer » et « Body ». L’ordre des balises « define » n’a pas d’importance. Il n’est pas obligatoire de redéfinir des parties. Si « JSFCoursFacelets » est le contexte du projet, cette page est accessible à l’adresse : http://localhost:18080/JSFCoursFacelets/PageMain.jsf Voici ce que donne cette page de manière graphique : http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 94 JSF - Framework Web 95 / 108 Attention : Dans la dernière page « PageMain.xhtml », toutes les balises <ui:define> qui se trouve en dehors de la première balise <ui:composition> sont ignorées. De plus tout le code qui ne se trouve pas à l’intérieur de balises <ui:define> valide est également ignoré. C’est-à-dire que le code suivant va générer la même page HTML. Toutes les parties grisées ne sont pas incluses dans la page HTML que reçoit le client. <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Page principale</title> </head> <body> <!-- balise ignoré --> <ui:define name="Title">Titre non pris en compte</ui:define> <!-- Seule balise composition valide --> <ui:composition template="MainTemplate.xhtml"> <!-- balise define valide --> <ui:define name="Title">Page principale</ui:define> Ce texte est ignoré <!-- balise define valide --> <ui:define name="Footer">Pied de page</ui:define> <!-- balise define valide --> <ui:define name="Body">Corps de la page main redéfini</ui:define> </ui:composition> <!-- balise ignoré --> <ui:define name="Menu">Menu non pris en compte</ui:define> <!-- cette balise composition est complètement ignoré --> <ui:composition template="MainTemplate.xhtml"> http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 95 JSF - Framework Web 96 / 108 <ui:define name="Menu">Menu</ui:define> <ui:define name="Body"> Encore le Corps </ui:define> </ui:composition> </body> </html> Le fonctionnement des différentes balises sera revu en détail dans la suite du cours. Il est possible en repartant de cet exemple de créer un squelette de page que vous pouvez réutiliser comme vous voulez. 9.5. Balises Facelets 9.5.1. Composition La balise <ui:composition> permet : • soit de définir une zone de template • soit d’utiliser un template en y apportant des modifications éventuelles (utilisation de l’attribut « template ») 9.5.1.1. Définition de template Prenons le premier cas pour définir une zone de template. Exemple : <ui:composition> Partie réutilisable </ui:composition> Il est possible d’utiliser les balises <ui:insert> et pour définir quelles zones du template sont modifiables. 9.5.1.2. Utilisation de template Un template peut s’utiliser avec la balise <ui:composition>. Dans ce cas, l’attribut « template » doit définir l’url vers le fichier de template à utiliser. L’utilisation de template suit les règles suivantes : • Si le fichier de template contient une ou plusieurs balises <composition> alors seulement le corps de la première balise est pris en compte. • Sinon c’est toute la page qui est prise en compte. Dans tous les cas, le code qui se trouve autour de la balise <composition> dans la page appelante n’est pas inclus dans la réponse finale. Voir le chapitre « 9.4 Templating de vue » pour un exemple d’utilisation de cette balise où seulement la page appelante utilise la balise <ui:composition>. Nous allons ici voir un 2ème exemple où la page de template et la page appelante utilisent une balise <ui:composition>. http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 96 JSF - Framework Web 9.5.1.3. 97 / 108 Exemple TemplateAvecComposition.xhtml : <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title></title> </head> <body> <ui:composition> <div style="..."> <ui:insert name="Header">Header</ui:insert> </div> <div style="..."> <ui:insert name="Body">Default body</ui:insert> </div> </ui:composition> </body> </html> UtiliseTemplateAvecComposition.xhtml : <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Page principale</title> </head> <body> <ui:composition template="TemplateAvecComposition.xhtml"> <ui:define name="Header">En-tête</ui:define> <ui:define name="Body">Corps de la page main redéfini</ui:define> </ui:composition> </body> </html> Le résultat sera : <div style="..."> En-tête </div> <div style="..."> Corps de la page main redéfini http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 97 JSF - Framework Web 98 / 108 </div> Ici on voit bien que tout ce qui est autour des balises <ui:composition> est ignoré. 9.5.2. Insert La balise <ui:insert> s’utilise dans un template. Elle permet de définir une portion de code qui peut être remplacé. L’attribut « name » permet de donner un nom à la portion. Le remplacement est ensuite effectué par la balise <ui:define> qui possède elle aussi un attribut « name ». Il suffit de faire correspondre les 2, pour effectuer un remplacement. Exemple : <ui:insert name="Partie1"> Partie modifiable </ui:insert> Il est également possible d’utiliser la balise sans spécifier de nom. Dans ce cas c’est tout le corps de la balise qui utilise le template qui sera inclus. Exemple sans nom : ParagrapheTemplate.xhtml : Début paragraphe <ui:insert /> Fin paragraphe Page.xhtml : <ui:composition template="Paragraphe.xhtml"> Milieu du paragraphe </ui:composition> La page générée sera : Début paragraphe Milieu du paragraphe Fin paragraphe 9.5.3. Define La balise <ui:define> permet de remplacer le contenu d’une balise <ui:insert>. Elle possède l’attribut obligatoire « name » qui doit correspondre au nom d’une balise <ui:insert>. 9.5.4. Decoration La balise <ui:decorate> permet d’utiliser un template. Elle fonctionne comme la balise <ui:composition> sauf que tout le code de la page appelante est inclus dans la réponse. Nous avons la même page de template que dans le chapitre « 9.5.1.3 Exemple ». TemplateAvecComposition.xhtml : http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 98 JSF - Framework Web 99 / 108 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title></title> </head> <body> <ui:composition> <div style="..."> <ui:insert name="Header">Header</ui:insert> </div> <div style="..."> <ui:insert name="Body">Default body</ui:insert> </div> </ui:composition> </body> </html> UtiliseTemplateAvecDecoration.xhtml : <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Page principale</title> </head> <body> <ui:decorate template="TemplateAvecComposition.xhtml"> <ui:define name="Header">En-tête</ui:define> <ui:define name="Body">Corps de la page main redéfini</ui:define> </ui:decorate> </body> </html> Le résultat sera : <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Page principale</title> </head> <body> <div style="..."> En-tête http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 99 JSF - Framework Web 100 / 108 </div> <div style="..."> Corps de la page main redéfini </div> </body> </html> 9.5.5. Include La balise <ui:include> permet d’inclure une page Facelets. Elle possède l’attribut obligatoire « src » qui désigne la page à inclure. Le nom de la page peut être écrit directement ou il peut s’agir d’une expression EL. Exemple qui permet d’inclure la page « Footer.xhtml » : <ui:include src="Footer.xhtml" /> L’inclusion suit les règles suivantes : • Si le fichier à inclure contient une ou plusieurs balises <composition> alors seulement l’intérieur de la première balise est pris en compte. • Sinon c’est toute la page qui est prise en compte. Ce mécanisme est intéressant car une page peut quand même définir la balise <html> avec tous les namespace, ce qui permet de l’éditer dans un éditeur. 9.5.6. Param La balise <ui:param> permet de passer des paramètres à un bloc de code Facelets. Elle peut s’utiliser à l’intérieur des balises <ui:define> et <ui :include>. Elle possède les 2 attributs obligatoires : • name • value 9.5.7. Remove La balise <ui:remove> permet de ne pas tenir compte de tout le code qui se trouve dans son corps. C’est comme si on mettait en commentaire tout un bloc de code (comme pour /* et */). Mais cette solution présente l’avantage de ne pas avoir à mettre chaque ligne de code en commentaire. Exemple : <body> <h:outputText value="affiché" /> <ui:remove> <h:outputText value="Pas affiché" /> <form> <%-- <h:outputText value="Encore pas affiché" /> --%> <!-- <input type="text" /> --> <button type="submit" /> </form> </ui:remove> http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 100 101 / 108 JSF - Framework Web <h:outputText value="affiché" /> </body> Ici toute la partie à l’intérieur de <ui:remove> n’est pas prise en compte. 9.5.8. Component La balise <ui:component> est identique à <ui:composition>, sauf tout le corps de la balise est inclus dans un composant JSF qu’on peut utiliser. Pour l’utiliser il faut passer par l’attribut « bindings ». 9.5.9. Fragment La balise <ui:fragment> est identique à la balise <ui:component> dans son fonctionnement, mais tout comme la balise <ui:decorate>, les balises de la page appelante seront incluses dans la réponse finale. 9.5.10. Balises JSTL et Fonctions Facelets propose une implémentation des taglibs JSTL Core et des fonctions. 9.5.10.1. JSTL Core Pour utiliser les balises JSTL Core, « xmlns:c="http://java.sun.com/jstl/core" ». il faut déclarer le namespace suivant : <c:if> Attribut Obligatoire Description test Oui Expression à évaluer. Si l’expression vaut true alors le corps de cette balise est pris en compte sinon il est ignoré. Une expression null vaut false. var non Variable qui doit contenir le résultat du test de l’expression. Exemple : <c:if test="#{user.roles['admin']}"> </c:if> <c:forEach> Attribut Description items Une expression qui pointe sur la Collection, la Map ou le tableau à parcourir. var Contient l’élément actuel qu’on parcourt http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 101 JSF - Framework Web begin Index à partir duquel il faut commencer l’itération. end Index à partir duquel il faut finir l’itération. step La valeur dont l’index augmente à chaque itération. varStatus Pointe sur un objet qui contient les propriétés suivantes : • boolean first, • boolean last, • int begin, • int end, • int index, • int step, 102 / 108 <c:set> Attribut Description var value La variable qui va contenir la valeur de l’attribut « value ». L’expression qui va déterminer la valeur de la variable. Cette expression sera réutilisée à chaque fois que la variable est utilisée. C’est-à-dire qu’il n’y a pas copie de cette valeur dans la variable désignée par l’attribut « var ». 9.5.10.2. Fonctions Pour utiliser les fonctions il faut déclarer le namespace suivant : « xmlns:fn="http://java.sun.com/jsp/jstl/functions" ». 9.6. Templating de composants 9.6.1. Le problème Malgré la simplicité de JSF, il existe de nombreux cas où l’on est obligé de répéter du code qui se ressemble beaucoup. C’est le cas pour tout ce qui est présentation, comme nous l’avons vu lors du chapitre « 9.4 Templating de vue », mais le problème peut aussi se poser pour tout ce qui est composants personnels. 9.6.2. La solution avec Facelets Grâce à son système de template, facelets va permettre de créer une balise que l’on va utiliser comme un composant. Cette balise va se charger de générer tout le code des différents duplicata, la seule http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 102 JSF - Framework Web 103 / 108 chose qu’il faudra lui fournir est la liste des attributs qui peuvent changer entre plusieurs duplicata de code. CaseComponent.xhtml : <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:c="http://java.sun.com/jstl/core" xmlns:fn="http://java.sun.com/jsp/jstl/functions"> <head> <title></title> </head> <body> <!-- Paramètres: - columnName: Nom de la colonne - sortMethod: Nom de la méthode du bean qui permet de trier la colonne - bean: bean qui contient la méthode de tri - caseValue: Valeur de la case --> <ui:composition> <h:column> <f:facet name="header"> <h:panelGroup> <!-- Si la méthode est triable, le nom est un lien qui permet de la trier --> <c:if test="#{!empty sortMethod}"> <h:commandLink action="#{bean[sortMethod]}"> <h:outputText value="#{columnName}" /> </h:commandLink> </c:if> <!-- Sinon on affiche juste son nom --> <c:if test="#{empty sortMethod}"> <h:outputText value="#{columnName}" /> </c:if> </h:panelGroup> </f:facet> <h:outputText value="#{caseValue}" /> </h:column> </ui:composition> </body> </html> 9.6.3. TagFile Pour utiliser ce template de composant à l’aide d’une balise, il faut le déclarer dans fichier de déclaration. Le fichier doit porter l’extension « .taglib.xml ». http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 103 JSF - Framework Web 104 / 108 labo-sun.taglib.xml : <?xml version="1.0"?> <!DOCTYPE facelet-taglib PUBLIC "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN" "facelet-taglib_1_0.dtd"> <facelet-taglib> <namespace>http://www.labo-sun.com/jsf</namespace> <tag> <tag-name>case</tag-name> <source>CaseComponent.xhtml</source> </tag> </facelet-taglib> Cette déclaration va permettre d’utiliser notre template avec une balise de type : <case columnName="" sortMethod="" caseValue="" bean="" /> Les attributs de la balise sont déterminés à partir des variables qui sont utilisées dans la page de template. Pour que Facelets charge ce fichier, il faut ajouter la déclaration suivante dans le fichier web.xml. <context-param> <param-name>facelets.LIBRARIES</param-name> <param-value> /WEB-INF/facelets/tags/arcmind.taglib.xml </param-value> </context-param> Déclaration <managed-bean> <managed-bean-name>beanTable</managed-bean-name> <managed-bean-class>com.labosun.facelets.BeanTable</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> </managed-bean> public class BeanTable { private List<Person> persons; private static Comparator<Person> personLastNameComparator = new Comparator<Person>() { public int compare(Person o1, Person o2) { return o1.getLastName().compareTo(o2.getLastName()); } }; private static Comparator<Person> personFirstNameComparator = new Comparator<Person>() { public int compare(Person o1, Person o2) { return o1.getFirstName().compareTo(o2.getFirstName()); } }; http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 104 105 / 108 JSF - Framework Web public BeanTable() { persons = new ArrayList<Person>(); persons.add(new Person("Bob", "Gnoliac")); persons.add(new Person("Thomas", "Anderson")); persons.add(new Person("Raymond", "Smith")); } //Getter et setter public void sortByFirstName() { Collections.sort(getPersons(), personFirstNameComparator); } public void sortByLastName() { Collections.sort(getPersons(), personLastNameComparator); } } Voilà, notre balise est prête à être utilisée. La déclaration se fait à l’aide de namespace de la manière suivante : <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:m="http://www.labo-sun.com/jsf"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Test column template</title> </head> <body> <h:form> <h:dataTable value="#{beanTable.persons}" var="person" border="1" rules="all"> <m:case columnName="Last name" sortMethod="sortByLastName" caseValue="#{person.lastName}" bean="#{beanTable}" /> <m:case columnName="First name" sortMethod="sortByFirstName" caseValue="#{person.firstName}" bean="#{beanTable}" /> </h:dataTable> </h:form> </body> </html> Voici le résultat final: Tri par nom Tri par prénom http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 105 JSF - Framework Web 9.7. 106 / 108 Paramètres Paramètres Description facelets.LIBRARIES Liste des chemins vers des librairies de tags Facelets séparées par des « ; ». Les chemins sont relatifs à la racine de l’application. Une librairie n’est chargée que lorsque la page qui l’utilise doit être compilé. Exemple : /WEB-INF/facelets/my.taglib.xml; /WEB-INF/facelets/car.taglib.xml; facelets.DECORATORS Une liste de classes (séparées par des ‘ ;’) du type « com.sun.facelets.tag.TagDecorator » avec un constructeur par défaut. Ces décorateurs seront chargés lorsque la première requête atteindra la vue « FaceletViewHandler ». Exemple : com.sun.facelets.tag.jsf.html.HtmlDecorator facelets.DEVELOPMENT Si true alors des informations de debugging sont affichées sur la page Web lorsqu’il y a une erreur. La valeur par défaut est false. facelets.BUFFER_SIZE La taille du buffer utilisée par le ResponseWriter. Attention, une taille trop petite de buffer ne permet pas d’afficher toute une page. Par défaut, la valeur de -1 permet de ne pas mettre de limite de taille. facelets.REFRESH_PERIOD Permet de définir l’intervalle de temps minimum que doit attendre le compilateur avant de vérifier si une page doit être recompilée (s’il y a eu des changements). La valeur de -1 permet d’empêcher le compilateur de vérifier s’il y a eu des changements une fois que la page est compilée. http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 106 JSF - Framework Web 107 / 108 La valeur par défaut est 2. facelets.RESOURCE_RESOLVER Permet de spécifier un ResourceResolver personnalisé. La valeur par défaut est : com.sun.facelets.impl.DefaultResourceResolver facelets.VIEW_MAPPINGS Une liste d’url (séparées par des ‘ ;’) qui seront gérées par Facelets. Si rien n’est précisé, toutes les requêtes sont gérées par Facelets. Dans le cas où une url, n’est pas gérée par Facelets, c’est la vue du parent par défaut (JSP) qui est utilisée. Attention si vous spécifiez des url, le mapping vers la Servlet FacesServlet, doit être mappé pour plusieurs types de fichiers. Ex : /faces/*;*.xhtml http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 107 JSF - Framework Web 108 / 108 10. Bibliographie La documentation des balises JSF se trouve à l’adresse : JSF 1.1 : http://java.sun.com/j2ee/javaserverfaces/1.1/docs/tlddocs/index.html JSF 1.2 : http://java.sun.com/j2ee/javaserverfaces/1.2/docs/tlddocs/index.html La javadoc de l’API JSF se trouve à l’adresse : JSF 1.1 : http://java.sun.com/j2ee/javaserverfaces/1.1/docs/api/index.html JSF 1.2 : http://java.sun.com/j2ee/javaserverfaces/1.2/docs/api/index.html Le tutorial de SUN sur J2EE 1.4 et qui inclut un tutorial sur JSF 1.1 est à l’adresse : http://java.sun.com/j2ee/1.4/docs/tutorial/doc/index.html Le tutorial de SUN sur JEE 5 et qui inclut un tutorial sur JSF 1.2 est à l’adresse : http://java.sun.com/javaee/5/docs/tutorial/doc/index.html Le site de Facelets : https://facelets.dev.java.net/ Le site de MyFaces : http://myfaces.apache.org/index.html Oracle ADF : http://www.oracle.com/technology/products/jdev/htdocs/partners/addins/exchange/jsf/index.html http://www.labo-sun.com Ce document est la propriété de Supinfo et est soumis aux règles de droits d’auteurs 108