Vous n'êtes pas connecté. Connexion
|
|
TP2 2011 EJB 3.1 / JSF2De $1Table des matières
IntroductionDans cette séance :
Ressources :
Première partie : étude du modèle de navigation PRG (Post Redirect Get)Objectifs de cette partie :
Important : dans ce TP vous utiliserez des backing bean de portée "Requête". Il faut toujours essayer de limiter au maximum la portée des beans pour éviter l'encombrement de la mémoire (ce qui peut être un problème pour les sites Web très fréquentés). Evidemment dans certains cas, la portée "Session" est la plus indiquée (par ex pour un backing bean qui gère les connexions par login/password). Exemple 1 : analyse d'un post tout simpleNous allons étudier une application qui contient 2 pages JSF qui se référencent :
Vous créerez un nouveau projet de type Web Application. Utilisez simplement le menu "new/JSF Page" pour créer les pages. Voici le code des deux pages en question : formulaire_1.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:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <h:head> <title>Application>formulaire_1</title> </h:head> <h:body> <h1>Application>formulaire_1</h1> <h:form> <h:outputLabel value="Entrez un nombre" for="nombre"/> <h:inputText id="nombre" value="#{bean.nombre}"/> <h:commandButton value="Valider" action="affichage_1"/> </h:form> </h:body> </html> affichage_1.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:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets"> <h:head> <title>Application>affichage_1</title> </h:head> <h1>Applicationaffichage_1</h1> <h:body> Les nombres : <br/> <ui:repeat value="#{bean.nombresSuivants}" var="n"> #{n}, </ui:repeat> <h:form> <h:commandLink action="formulaire_1" value="Saisir un autre nombre"/> </h:form> </h:body> </html> Ces pages utilisent un Backing Bean (son nom est "bean") pour stocker la propriété correspondant au champs de formulaire "nombre". C'est la ligne suivante dans index.html qui correspond à la propriété : <h:inputText id="nombre" value="#{bean.nombre}"/> Voici donc le code du backing bean (que nous avons, dans cet exemple, mis dans le package "jsf") : Bean.java : package jsf; import java.util.ArrayList; import java.util.List; import javax.inject.Named; import javax.enterprise.context.RequestScoped; @Named(value = "bean") @RequestScoped public class Bean { private int nombre; public int getNombre() { return nombre; } public void setNombre(int nombre) { this.nombre = nombre; } public List<Integer> getNombresSuivants() { int nb = 5; ArrayList<Integer> t = new ArrayList(nb); for (int i = nombre; i < nombre + nb; i++) { t.add(i); } return t; } } Regardez déjà comment cet exemple s'articule. Notez dans les pages JSF l'utilisation de <ui:repeat ... /> qui correspond au tag JSTL vu l'an dernier <c:foreach... />. Notez que la collection sur laquelle on itère, donnée par : #{bean.nombresSuivants} ...correspond encore à un accès à une propriété, la propriété "nombresSuivants" qui existe puisqu'une méthode getNombresSuivants() existe. Rappelons que ce sont les getters et setters qui définissent une propriété et non pas l'existence d'une variable d'instance dans la classe. En particulier ici on a pas de variable d'instance. Exécutez cet exemple :
ATTENTION, si le message d'erreur: /formulaire_1.xhtml @17,58 value="#{bean.nombre}": Target Unreachable, identifier 'bean' resolved to null apparaît, c'est surement que vous n'avez pas coché la case "Use Context and Dependency Injection" lors de la création de l'application web. Pour résoudre cette erreur, il suffit j'ajouter un fichier nommé beans.xml dans le répertoire WEB-INF qui contient seulement les quelques lignes suivantes : <?xml version="1.0" encoding="UTF-8"?> <beans 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/beans_1_0.xsd"> </beans> Remarquez-vous des anomalies quant à l'URL affiché par le navigateur ? Rechargez la page qui affiche les 5 nombres. Que fait le navigateur ? Expliquez. Est-ce possible de mettre un bookmark sur la page affichage pour un certain nombre. Par exemple 5 ; la page affichage affichera les nombres 6 à 10 qui sont censés représenter une information importante sur le nombre 5... Et oui, cela ne marche pas ! Ce TP étudie un modèle, appelé PRG, pour ne plus avoir ce genre de problème. Exemple 2 : utilisation de la redirection (modèle PRG)Dans cet exemple nous allons faire une implémentation du modèle PRG vu en cours : la page résultat est affiché par une redirection (GET) après le POST. On va commencer par régler le problème de l'URL. Pour cela, on va changer le comportement par défaut de JSF qui utilise le "forward" pour faire afficher les pages JSF (voir cours). Pour utiliser une redirection il suffit d'ajouter aux URLs dans les pages JSF "faces-redirect=true", regardez le passage du cours à ce sujet.
Pour tester, lancez l'application et, à la main, tapez le bon URL pour faire afficher la page JSF formulaire_2.xhtml (note vous pouvez aussi éditer la page index.html et vous faire un petit menu avec des liens vers les différentes pages formulaire_1.xhtml, formulaire_2.xhtml etc...). Est-ce que les URL sont corrects maintenant ? Rechargez la page qui affiche les nombres. Avez-vous toujours le problème de l'exercice précédent ? Est-ce que les bons nombres sont bien affichés ? Expliquez. Exemple 3 : utilisation de la mémoire flashDans cet exemple nous allons utiliser la "mémoire flash" pour faire afficher les bons nombres dans la 2ème page. Pour ne pas toucher aux pages déjà écrites, mettez le suffixe "_3" aux nouvelles pages que vous allez écrire. Principe : dans le formulaire de la page formulaire_3.xhtml nous allons associer au champs input nom pas une propriété d'un backing bean (comme #{bean.nombre}) mais nous allons positionner cette variable dans une mémoire temporaire qui va durer un peu plus que la durée de la requête : la fameuse "mémoire flash". Au lieu de #{bean.nombre} nous allons mettre #{flash.nombre} : formulaire_3.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:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <h:head> <title>Application>formulaire_3</title> </h:head> <h:body> <h1>Applicationformulaire_3</h1> <h:form> <h:outputLabel value="Entrez un nombre" for="nombre"/> <h:inputText id="nombre" value="#{flash.nombre}"/> <h:commandButton value="Valider" action="affichage_3?faces-redirect=true"/> </h:form> </h:body> </html> Lorsque le formulaire est validé, on appelle bien la page affichage_3.xhtml qui est comme ceci (seule différence on ne reférence pas #{bean.nombre} mais #{bean3.nombre} qui est la nouvelle version du backing bean, source un peu plus loin : affichage_3.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:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets"> <h:head> <title>Application>affichage_3</title> </h:head> <h:body> <h1>Application>affichage_3</h1> Les nombres : <br/> <ui:repeat value="#{bean3.nombresSuivants}" var="n"> #{n}, </ui:repeat> <h:form> <h:commandLink action="formulaire_3?faces-redirect=true" value="Saisir un autre nombre"/> </h:form> </h:body> </html> Vous aurez besoin de modifier le backing bean. Pour ne pas perdre le précédent, créez un nouveau bean "Bean3.java" pour cet exercice : package jsf; import java.util.ArrayList; import java.util.List; import javax.inject.Named; import javax.enterprise.context.RequestScoped; import javax.faces.context.FacesContext; @Named(value = "bean3") @RequestScoped public class Bean3 { public List<Integer> getNombresSuivants() { int nb = 5; // UTILISATION DE LA MEMOIRE FLASH String s = (String)FacesContext.getCurrentInstance() .getExternalContext().getFlash().get("nombre"); int n = 0; if (s != null) { n = Integer.parseInt(s); } ArrayList<Integer> t = new ArrayList(nb); for (int i = n; i < n + nb; i++) { t.add(i); } return t; } } Les lignes qui suivent le commentaire "UTILISATION DE LA MEMOIRE FLASH" indiquent juste qu'on va récupérer la valeur d'une propriété "nombre" dans cet objet "magique" qu'est "la mémoire flash", une mémoire qui dure un peu plus que la durée d'une requête. Exécutez l'application !
La mémoire flash peut être utile pour conserver facilement une information dans la requête suivante (et on peut même prolonger encore plus avec "keep" ; voir le cours). Nous allons maintenant voir une méthode un peu plus élégante pour arriver à nos fins tout en résolvant le problème du bookmark/marque page. Exemple 4 : utilisation des paramètres de vueDans cet exercice nous allons utiliser une toute autre façon pour résoudre le problème du passage de la valeur tout en permettant l'affichage synchronisé de l'URL dans le navigateur. Nous allons aussi régler le dernier problème : il sera possible de conserver les informations dans un marque-page du navigateur. Reprenez le code de l'exercice sur la redirection (nous n'allons plus utiliser la mémoire flash dans cet exercice). Mettez le suffixe 4 aux pages utilisées par cet exercice : formulaire_4.xhtml et affichage_4.xhtml Dans la page formulaire_4.xhtml nous allons réutiliser le managedbean bean, et indiquer :
formulaire_4.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:h="http://java.sun.com/jsf/html"> <h:head> <title>Application>formulaire_4</title> </h:head> <h:body> <h1>Applicationformulaire_4</h1> <h:form> <h:outputLabel value="Entrez un nombre" for="nombre"/> <h:inputText id="nombre" value="#{bean.nombre}"/> <h:commandButton value="Valider" action="affichage_4?faces-redirect=true&includeViewParams=true"/> </h:form> </h:body> </html> Dans affichage_4.xhtml qui affiche les 5 nombres on ajoutera un paramètre de vue pour récupérer le nombre saisi par l'utilisateur : affichage_4.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:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core"> <f:metadata> <f:viewParam name="nombre" value="#{bean.nombre}"/> </f:metadata> <h:head> <title>Applicationaffichage_4</title> </h:head> <h:body> <h1>Application>affichage_4</h1> Les nombres : <br/> <ui:repeat value="#{bean.nombresSuivants}" var="n"> #{n}, </ui:repeat> <h:form> <h:commandLink action="formulaire_4?faces-redirect=true" value="Saisir un autre nombre"/> </h:form> </h:body> </html> Testez votre application. Etudiez en particulier les URL affichés par le navigateur. Est-ce possible de garder un marque-page pour garder, par exemple, les 5 nombres qui suivent le nombre 34 ? Après avoir testé, ajoutez le fait que le nombre précédemment saisi apparaisse lorsque l'utilisateur saisit un autre nombre ! Templating: mise en place de templates de présentationRessources : http://www.coreservlets.com/JSF-Tutorial/jsf2/#Templating Prenez le temps de remarquer dans les pages jsf tout le code qui se répète... dans les titres et les h1, et dans la structure des pages... De plus, j'imagine que la plupart d'entre vous doit encore modifier les urls à la main pour essayer une page formulaire_X au démarrage de l'application. Nous allons maintenant mettre en place des templates de présentation, pour alléger tout ce code et rendre la navigation pratique. Les templates sont des "pages types" dans lesquelles ont peut définir des zones que l'on pourra remplacer dans les pages qui se réfèrent à ces pages types. En outre, dans un modèle on peut définir des paramètres (en gros, des variables qui pourront être remplacées par des valeurs, spécifiées dans les pages qui utilisent ces modèles). Et enfin, un modèle peut lui même réutiliser un autre modèle, l'enrichir etc. Voici ce que l'on désire obtenir : C'est une page avec une zone en haut, qui contient un titre (ici la description de la page affichée dans la partie centrale, dans une pseudo hiérarchie application/menu du tp2). La page qui utilise le modèle est ici index.xhtml comme l'indique l'URL. Dans cette page on a indiqué que l'on utilisé le modèle + on a indiqué que la variable qui décrivait le contenu valait Application/Menu du TP2. Le même modèle va être repris par deux autres modèles : un pour les formulaires de saisie des nombres, l'autre par les pages qui affichent les nombres qui suivent le nombre entré par l'utilisateur. Exemples de résultats, ici la page de saisie du nombre du premier exercice (cette page formulaire_1.xhtml utilise le modèle de formulaire, qui utilise le modèle par défaut) : et la page d'afficahe correspondante (qui utilise un modèle pour l'affichage, qui lui aussi utilise le modèle par défaut) : etc... vous avez compris le principe, normalement on va pouvoir factoriser au maximum le code et le nombre de lignes dans les pages .xhtml devrait être réduit en conséquence (en plus d'avoir une présentation plus jolie). Comment arriver à ce résultat ? Travail à faire : Commencez par créer une page de type Facelets Template (menu File/New File/java server faces etc), que vous nommerez template_default. Choisissez un layout avec header et barre latérale pour la navigation, puis copiez-y le code suivant: template_default.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"> <h:head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <link href="./resources/css/default.css" rel="stylesheet" type="text/css" /> <link href="./resources/css/cssLayout.css" rel="stylesheet" type="text/css" /> <title>Application><ui:insert name="complementTitreEtH1"/></title> </h:head> <h:body> <div id="top" class="top"> <h1>Application><ui:insert name="complementTitreEtH1"/></h1> </div> <div> <div id="left"> <ul> <li><a href="index.xhtml">accueil</a></li> <li><a href="formulaire_1.xhtml">formulaire_1</a></li> <li><a href="formulaire_2.xhtml">formulaire_2</a></li> <li><a href="formulaire_3.xhtml">formulaire_3</a></li> <li><a href="formulaire_4.xhtml">formulaire_4</a></li> </ul> </div> <div id="content" class="left_content"> <ui:insert name="content">Content</ui:insert> </div> </div> </h:body> </html> Vous pouvez voir le rendu de ce template en cliquant droit dans le code / view. Maintenant, nous allons utiliser ce template par défaut :
Pour la page index.xhtml, supprimez l'actuelle page index.xhtml créez une nouvelle page Facelet Template Client, nommée index, qui utilise le template qu'on vient juste de créer, et de type generate root tag html. remplacez le slot nommé complementTitreEtH1 (il ne doit y en avoir qu'un du même nom !) par "Accueil" ; et remplacez le slot nommé content par "Exercices PRG et templating". Pour le template des autres pages, créez un nouveau Facelet Template Client, nommé template_tests, qui utilise le tempate template_default, et de type generate root tag ui:composition. Le contenu de cette page est le suivant: template_tests.xhtml <?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <ui:composition xmlns:ui="http://java.sun.com/jsf/facelets" template="./template_default.xhtml"> <ui:define name="complementTitreEtH1">Test#{paramNumeroDuTest}>#{paramTypePage}</ui:define> </ui:composition> Notez que les paramètres paramNumeroDuTest et paramTypePage pourront être définies à l'aide d'un tag ui:param dans les pages clientes de ce template. Maintenant, on va définir encore deux templates à partir de template_tests: template_formulaire et template_affichage. Vous pouvez simplement créer deux nouvelles pages xhtml et copier le contenu suivant dedans: template_formulaire.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"> <body> <ui:composition template="./template_tests.xhtml"> <ui:param name="paramTypePage" value="Formulaire"/> <ui:define name="content"> <h:form> <h:outputLabel value="Entrez un nombre" for="nombre"/> <h:inputText id="nombre" value="#{paramValue}"/> <ui:insert name="commandButton">commandButton</ui:insert> </h:form> </ui:define> </ui:composition> </body> </html> Ici on a défini le paramètre paramTypePage à la valeur Formulaire (tag ui:param), on a spécifié un peu plus le slot content, en rajoutant un paramètre paramValue et un slot commandButton. Les pages formulaire_X clientes de ce template n'auront plus qu'à définir: le paramètre paramNumeroDeTest, le paramètre paramValue, et le slot commandButton. template_affichage.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"> <body> <ui:composition template="./template_tests.xhtml"> <ui:param name="paramTypePage" value="Affichage"/> <ui:define name="content"> Les nombres : <br/> <ui:repeat value="#{paramRepeatValue}" var="n"> #{n}, </ui:repeat> <h:form> <ui:insert name="commandLink">commandLink</ui:insert> </h:form> </ui:define> </ui:composition> </body> </html> ici de la même manière on a défini le paramètre paramTypePage à la valeur Affichage (tag ui:param), on a spécifié un peu plus le slot content, en rajoutant un paramètre paramRepeatValue et un slot commandLink. Les pages formulaire_X clientes de ce template n'auront plus qu'à définir: le paramètre paramNumeroDeTest, le paramètre paramRepeatValue, et le slot commandLink. Travail à faire: maintenant si vous avez compris ces exemples, vous avez toutes les cartes en main pour réécrire les pages formulaire_X.xhtml comme clientes du template template_formlaire, et les pages affichage_X.xhtml comme clientes du template template_affichage. Vous pourrez constater qu'on a bien factorisé le code !!! Pour ce qui est du numero de test 4, il suffit d'ajouter les lignes <f:metadata> <f:viewParam name="nombre" value="#{bean.nombre}"/> </f:metadata> à l'intérieur du tag h:html, AVANTle tag ui:composition. à vous de jouer !!!
Correction des exercices précédents
Deuxième partie : étude d'une correction du TP1Pour tout le monde, pour éviter les problèmes de compatibilité rencontrés la semaine dernière, remplacez votre base de donnée locale sample (dossier .netbeans-derby) par celle ci. Dans cette correction la plupart des problèmes évoqués à la fin du TP1 ont disparu :
Travail à faire :
|
Powered by MindTouch Deki Open Source Edition v.8.08 |