Vous n'êtes pas connecté. Connexion
|
|
Accueil > Intranet Michel Buffa > Cours composants distribués pour l'entreprise 2014-2015 > TP JSF 2 2014-2015 > TP JSF 2 2014-2015
TP JSF 2 2014-2015De $1Table des matières
IntroductionDans cette séance :
Ressources : En plus du support de cours JSF disponible sur la page du cours, je vous propose les ressources suivantes :
Etude du modèle de navigation PRG (Post Redirect Get)NOTE : un projet contenant les corrections de cette section (navigation) + de la section suvante (templating) est donnée vers la fin de ce TP. 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, les portées "Vue", "Conversation", ou même "Session" sont plus indiquées (par ex la portée "Session" pour un backing bean qui gère les connexions par login/password). Création et initialisation du projet1 - Cette fois-ci vous créerez un nouveau projet de type Web Application en choisissant "Java Web", puis "Web Application" (pas "Java EE", puis "Entreprise Application" comme dans le TP 1). 2 - N'oubliez pas de cocher le Framework "Java Server Faces" lors de la création du projet. Pas d'utilisation de PrimeFaces pour ce TP. 3 - Ajoutez dans le projet un fichier beans.xml (click droit / new / Context and dependency injection / fichier beans.xml), sans ce fichier certaines injections de code ne fonctionnent pas. 4 - ATTENTION : Netbeans a "oublié" d'inclure la librairie permettant certaines injections de code, nécessaire aux projets utilisant le scope "SessionScoped", vous ajouterez donc à votre projet, manuellement, la librairie : cdi-api.jar
Vous êtes prêts à travailler... Exemple 1 : analyse d'un post tout simpleNous allons étudier une application qui contient 2 pages JSF qui se référencent :
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://xmlns.jcp.org/jsf/html" xmlns:f="http://xmlns.jcp.org/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 Vous comprenez à quoi sert l'attribut varstatus de <ui:repeat>, utilisé dans l'expression EL #{! status.last ? ', ' : ''} ? <?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://xmlns.jcp.org/jsf/html" xmlns:ui="http://xmlns.jcp.org/jsf/facelets"> <h:head> <title>Application>affichage_1</title> </h:head> <h:body> <h1>Application>affichage_1</h1> Les nombres : <br/> <ui:repeat value="#{bean.nombresSuivants}" var="n" varStatus="status"> #{n + 1}#{! status.last ? ', ' : ''} </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 formulaire_1.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"). Pour l'ajouter au projet utilisez le menu "nouveau / jsf managed bean". Notez bien les import pour "Named" et "RequestScoped". Pour la plupart des "scopes", à par le "ViewScoped" vu lors du TP1, nous utiliserons ces "chemins d'import". Si lorsque vous créez par vous-même un "jsf managed bean" vous avez @ManagedBean au lieu de @Named, c'est qu'il vous manque le fichier beans.xml dans WEB-INF, ajoutez le alors au projet pour ne plus être embêté. 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; List<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 qu'il vous manque beans.xml. On vous l'a dit, ce fichier est nécessaire à tous les projets "JSF" modernes. C'est une bonne pratique de l'avoir par défaut dans tous les projets. Même si ce n'est pas ce que NetBeans propose par défaut. <?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. Nous avons donc réglé le problème des URL et de la double soumission du formulaire mais l'application ne fonctionne plus pour l'affichage des bons nombres. Nous allons maintenant voir une méthode élégante pour arriver à nos fins et pouvoir conserver une page d'affichage dans un bookmark, tout en conservant les bons URL et sans avoir la double soumission du formulaire. Exemple 3 : 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. Mettez le suffixe 3 aux pages utilisées par cet exercice : formulaire_3.xhtml et affichage_3.xhtml Comment passer la valeur de la propriété nombre d'une instance du bean à une autre (n'oubliez pas que la portée du bean est la requête et ce sont donc 2 instances différentes de la classe Bean qui sont utilisées par les 2 pages JSF) ? Nous allons utiliser un paramètre de vue (comme dans le TP 1) :
Ainsi la page qui affiche les nombres sera bookmarkables/ajoutable dans le marque page. Remarque : includeViewParams simplifie les choses lorsque les paramètres sont nombreux ; pour ce cas, avec un seul paramètre, vous auriez aussi pu ajouter plus simplement le passage du paramètre nb en appelant une méthode action du bean (une méthode est obligatoire car il est interdit de mélanger dans l'attribut action une destination avec la valeur d'un paramètre) qui retourne la valeur 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://xmlns.jcp.org/jsf/html"> <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="#{bean.nombre}"/> <h:commandButton value="Valider" action="affichage_3?faces-redirect=true&includeViewParams=true"/> </h:form> </h:body> </html> Dans affichage_3.xhtml qui affiche les 5 nombres on ajoutera un paramètre de vue pour récupérer le nombre saisi par l'utilisateur : 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://xmlns.jcp.org/jsf/html" xmlns:ui="http://xmlns.jcp.org/jsf/facelets" xmlns:f="http://xmlns.jcp.org/jsf/core"> <f:metadata> <f:viewParam name="nb" value="#{bean.nombre}"/> </f:metadata> <h:head> <title>Applicationaffichage_3</title> </h:head> <h:body> <h1>Application>affichage_3</h1> Les nombres : <br/> <ui:repeat value="#{bean.nombresSuivants}" var="n" varStatus="status"> #{n + 1}#{! status.last ? ', ' : ''} </ui:repeat> <h:form> <h:commandLink action="formulaire_3?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. Si les nombres affichés sont toujours 1, 2, 3, 4, 5 c'est sans doute que vous avez une version de Mojarra antérieure à la version 2.2.2 (bug déjà vu dans le TP 1). Vous savez comment résoudre ce problème : dans la page affichage_3.xhtml, remplacez xmlns.jcp.org par java.sun.com dans l'espace de noms de f (xmlns:f="http://xmlns.jcp.org/jsf/core"). Est-ce possible de garder un marque-page pour garder, par exemple, les 5 nombres qui suivent le nombre 34 ? Testez. Exemple 4 : idem mais...Modification très simple de l'exercice 3: Reprenez le code de l'exercice 3, mettez le suffixe 4 aux pages utilisées par cet exercice : formulaire_4.xhtml et affichage_4.xhtml Templating: mise en place de templates de présentationRessources : http://www.coreservlets.com/JSF-Tutorial/jsf2/#Templating ce site contient tout un ensemble de cours sur JSF. Mais le PDF sur le templating, rien que la page 8, montre le principe général des includes. 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'affichage 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, choses à remarquer :
Copiez-donc ce code pour le template par défaut : 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" /> <h:outputStylesheet library="css" name="default.css"/> <h:outputStylesheet library="css" name="cssLayout.css"/> <title>Application><ui:insert name="complementTitreEtH1"/>#{numEx}</title> </h:head> <h:body> <div id="top" class="top"> <h1>Application><ui:insert name="complementTitreEtH1"/>#{numEx}</h1> </div> <div> <div id="left"> <ul> <li><h:link outcome="index.xhtml" value="accueil"/></li> <li><h:link outcome="formulaire_1.xhtml" value="formulaire_1"/></li> <li><h:link outcome="formulaire_2.xhtml" value="formulaire_2"/></li> <li><h:link outcome="formulaire_3.xhtml" value="formulaire_3"/></li> <li><h:link outcome="formulaire_4.xhtml" value="formulaire_4"/></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. Ce template définit trois espaces libres que ses clients pourront spécifier:
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 (clic droit, New/Java Server Faces/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 une dissertation sur le bonheur que procure la simplicité de programmer avec les facelets. Pour les autre templates créez deux nouveaux Facelet Templates Clients, nommés template_formulaire et template_affichage, qui utilisent le template "template_default", et de type generate root tag ui:composition. Le contenu de ces pages sont les suivants:
template_formulaire.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="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" template="./template_default.xhtml"> <ui:define name="complementTitreEtH1">formulaire_</ui:define> <ui:define name="content"> <h:form> <h:outputLabel value="Entrez un nombre" for="nombre"/> <h:inputText id="nombre" value="#{bean.nombre}"/> <h:commandButton value="Valider" action="#{bean.actionString('affichage', numEx, query)}"/> </h:form> </ui:define> </ui:composition>
template_affichage.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="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" template="./template_default.xhtml"> <ui:define name="complementTitreEtH1">affichage_</ui:define> <ui:define name="content"> Les nombres : <br/> <ui:repeat value="#{bean.nombresSuivants}" var="n"> #{n + 1}, </ui:repeat> <h:form> <h:commandLink value="Saisir un autre nombre" action="#{bean.actionString('formulaire', numEx, query)}"/> </h:form> </ui:define> </ui:composition> Ces deux templates spécifient les champs complementTitreEtH1 et content, donc les pages formulaire_X.xhtml et affichage_X.xhtml n'auront à priori que le paramètre numEx à spécifier, et on fait ça de la manière suivante: <ui:param name="numEx" value="2"/> <ui:param name="numEx" value="X"/> Mais attention, regardez bien: on est obligé de calculer la valeur de l'attribut action du commandLink et du commandButton ! On doit donc passer par le backing bean de l'application: Bean. Vous implémenterez donc la méthode actionString dans le backingbean, qui doit avoir pour signature: public String actionString(String base, String numEx, String query); où:
Travail à faire: Maintenant si vous avez bien tout suivi jusque là, et que vous n'avez pas perdu trop de temps avec dissertation sur le bonheur que procure la simplicité de programmer avec les facelets, vous n'avez plus qu'à écrire la méthode actionString dans le backingBean, et vous pourrez réécrire les pages formulaire_X.xhtml comme clientes du template template_formulaire, et les pages affichage_X.xhtml comme clientes du template template_affichage. Si NetBeans dit que template_formulaire.xhtml n'est pas un template, le plus simple est de désigner template_default comme template et ensuite d'aller directement dans le fichier pour changer le template. Ensuite, copiez les pages d'affichage et de formulaire et modifiez-les pour en faire les versions suivantes. Vous pourrez constater à quel point les pages sont devenues super simples !!! Pour les numeros d'exercices 3 et 4, n'oubliez pas d'ajouter les lignes <f:metadata> <f:viewParam name="nombre" value="#{bean.nombre}"/> </f:metadata> à l'intérieur du tag ui:composition lorsque c'est nécessaire ! à vous de jouer !!!
Mots clés:
|
Powered by MindTouch Deki Open Source Edition v.8.08 |