Vous êtes sur la page 1sur 27

Plan de ce cours

 Présentation de JSF  Événements


JSF  Architecture des applications JSF  Ajax
(Java Server Faces)  Exemple de page JSF  JSF et HTML5
 Backing bean et portée  Ressources
 Cycle de vie
ITU - Université de Nice Sophia Antipolis  Fichiers de configuration
Richard Grin  Navigation
Version O 2.2 – 17/2/16
 PRG
 Messages d’erreur ou d’information
R. Grin JSF page 2

JSF
 Framework Java pour le développement
d’applications Web
 Les pages HTML vues par le client Web sont

Présentation de JSF
représentées sur le serveur par des composants Java
 Ces composants Java sont décrits par une page JSF

R. Grin JSF page 3 R. Grin JSF page 4

Page JSF EL et backing bean


 Fichier XHTML « Facelets » qui contient des balises
 EL (Expression Language) : langage utilisé pour
qui décrivent des composants Java représenter des données dynamiques ou des
 Traduite en une page HTML envoyée au client Web traitements à exécuter dans les pages JSF
 Les expressions EL sont encadrées par #{ }
 Une expression EL peut utiliser un backing bean,
classe Java :
 une donnée dynamique correspond à une

propriété du backing bean


 un traitement correspond à une méthode du

backing bean
R. Grin JSF page 5 R. Grin JSF page 6

1
Exemple - page JSF form.xhtml Exemple - page JSF form.xhtml
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 ..." <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 ..."
"http://www.w3.org/TR/xhtml1/DTD/xhtml1- ..."> "http://www.w3.org/TR/xhtml1/DTD/xhtml1- ...">
<html xmlns="http://www.w3.org/1999/xhtml" <html xmlns="http://www.w3.org/1999/xhtml" Espace de
xmlns:h="http://xmlns.jcp.org/jsf/html"> xmlns:h="http://xmlns.jcp.org/jsf/html"> noms JSF
<h:head> <title>Présentation</title> </h:head> <h:head> <title>Présentation</title> </h:head>
<h:body> <h:body>
Lien avec la propriété
<h3>Présentation</h3> <h3>Présentation</h3>
<h:form> <h:form>
nom du backing bean
Nom : <h:inputText value="#{utilisateur.nom}"/> Nom : <h:inputText value="#{utilisateur.nom}"/>
<h:commandButton value="Enregistrer" <h:commandButton value="Enregistrer"
action="#{utilisateur.direHello()}"/> action="#{utilisateur.direHello()}"/>
</h:form> </h:form>
</h:body> </h:body> Méthode direHello
Vous devinez ce que fait cette page ?
</html> </html> utilisateur est le du backing bean
R. Grin JSF page 7 R. Grin nom d’un backing beanJSF page 8

Exemple - Backing bean Exemple - Backing bean


Nom par défaut : Portée du bean ; il sera supprimé à la fin de la requête
@Named « utilisateur » @Named permet au backing bean @Named
@RequestScoped d’être utilisé dans une expression EL @RequestScoped
public class Utilisateur { public class Utilisateur { <h:inputText
private String nom; private String nom; value="#{utilisateur.nom}"/>
public String getNom() { return nom; } public String getNom() { return nom; }
public void setNom(String nom) { public void setNom(String nom) {
Propriété nom
this.nom = nom; this.nom = nom;
} }
public String direHello() { public String direHello() {
return "hello"; return "hello";
} Instance du bean automatiquement créée } <h:commandButton value="Enregistrer"
... par le container si elle n’existe pas déjà : ... action="#{utilisateur.direHello()}"/>
} #{utilisateur.xxx} } Nom de la prochaine
R. Grin JSF page 9 page à afficher
R. Grin JSF page 10

Exemple - page JSF hello.xhtml Page JSF – « Vue » Java


Que fait cette page ?
<?xml version="1.0" encoding="UTF-8"?> UiViewRoot
Composants Java
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 ..."
"http://www.w3.org/TR/xhtml1/DTD/xhtml1- ..."> HtmlOutputBody
HtmlOutputHead
<html xmlns="http://www.w3.org/1999/xhtml" <h:head>
xmlns:h="http://xmlns.jcp.org/jsf/html"> <title>Hello</title>
HtmlMessage HtmlForm
<h:head> <title>Hello</title> </h:head> </h:head>
<h:body> <h:body>
<h3>Hello</h3> Page retournée à la fin du <h3>Hello</h3> HtmlInputText HtmlCommandButton

Hello #{utilisateur.nom} traitement de la requête ; c’est <h:form>


<h:inputText value="#{utilisateur.nom}"/>
</h:body> donc le même backing bean <h:commandButton value="Enregistrer"
</html> que celui utilisé par la page
Lien avec la propriété
action="direHello"/>

nom du bean Java form.xhtml. nom contient </h:form>


</h:body>
le nom saisi par l’utilisateur
R. Grin JSF page 11 R. Grin JSF page 12

2
Principaux services rendus par JSF Les traitements
 Conversion entre le texte de l’interface utilisateur et  Séparation très nette entre
les valeurs Java du serveur
 les pages JSF
 Validation des données saisies par l’utilisateur
 les traitements à exécuter
 Automatisation de l’affichage des messages d’erreur
 Même les traitements directement liés à
si problèmes de conversion ou validation
l’interface utilisateur sont effectués en dehors de
 Internationalisation
la page JSF, par du code Java (backing bean)
 Support d’Ajax sans programmation
 Templates graphiques
 Librairies de composants

R. Grin JSF page 13 R. Grin JSF page 14

Répartition des tâches


 Pages JSF pour l’interface avec l’utilisateur ; ne
contiennent aucun traitement
Architecture des applications JSF  Backing beans pour les traitements liés
directement à l’interface utilisateur
 Backing beans font appel à des EJB ou POJO pour
les autres traitements (traitements métier, accès
aux sources de données)
 Accès aux bases de données utilisent souvent JPA
et donc les classes entités

R. Grin JSF page 15 R. Grin JSF page 16

Backing bean Code d’un backing bean


 Propriétés pour valeurs à afficher ou saisies par
 Souvent, mais pas obligatoirement, un backing l’utilisateur :
bean par page JSF <h:inputText value="#{utilisateur.nom}"/>
 Géré par le container CDI setter et getter utilisés en interne
Qu’est-ce que ça signifie ?  Traitements lancés par l’utilisateur, par un clic
bouton par exemple :
Le container crée le backing bean et le supprime <h:commandButton value="Enregistrer"
A quel moment le backing bean est créé par le container ? action="#{utilisateur.direHello()}"/>
La 1ère fois qu’il y a une référence au backing bean  Expression qui indique si un composant doit être affiché :
<h:inputText
A quel moment le container supprime le bean ? value="#{bean.client.pourcentageRemise}"
A la fin de sa portée rendered="#{bean.client.bonClient}" />
R. Grin JSF page 17 R. Grin JSF page 18

3
EJB Servlet « Faces »
 Lorsqu’un processus métier ou un accès aux  Le servlet JSF gère le cycle de vie JSF
bases de données doit être déclenché, le backing (fondamental pour JSF)
bean cède la main à un EJB  Les requêtes qui demandent une page JSF lui
 Un EJB est totalement indépendant de l’interface sont envoyées (par défaut URLs
graphique /faces/*, *.jsf, *.faces)
 Il est caché au développeur

R. Grin JSF page 19 R. Grin JSF page 20

Template Template avec 2


parties variables,
 Les pages d’un site Web respectent souvent une titre et
charte graphique contenu
 L’aspect commun aux pages est défini dans un
fichier XHTML à part, appelé template
 Un template contient des parties « variables » :
<ui:insert name="header"/>
 Une page JSF qui utilise ce template (page cliente Template
du template) a l’aspect défini par le template ;
elle définit les parties variables : 2 pages clientes
<ui:define name="header">...</ui:define> du Template
R. Grin JSF page 21 R. Grin JSF page 22

Conversion et validation Convertisseurs et validateurs


 Les données saisies dans les formulaires sont  Les convertisseurs et validateurs peuvent être
des chaînes de caractères fournis
 par JSF (validateurs standards)
 Il faut les convertir dans le bon type Java (celui
de la propriété du backing bean qui va les  par une classe (balise <f:validator>) ou une

contenir) méthode (attribut validator) de l’application


 Les données doivent être validées (par exemple  JSF intègre aussi la « validation des beans » ; on
un âge ou un email tapé par l’utilisateur) avant peut annoter les champs d’une classe (backing
d’exécuter les traitements qui les utilisent bean ou entité) pour donner une contrainte à
respecter :
@Size(min=2)
private String nom;
R. Grin JSF page 23 R. Grin JSF page 24

4
Exemple de validateur standard Composants des pages JSF
<h:inputText id="email" value="#{bean.email}"  JSF fournit de nombreux composants standards
required="true" label="Email"
validatorMessage="Email n’est pas valide">  Correspondent aux balises HTML : saisie des
<f:validateRegex données, listes déroulantes, boutons, formulaires,
pattern="^[_A-Za-z0-9-\+]+(\.[_A-Za-z0-9-]+)*@[A-
Za-z0-9-]+(\.[A-Za-z0-9]+)*(\.[A-Za-z]{2,})$" /> tables,…
</h:inputText>  Pour améliorer ces composants ou pour en avoir
<h:message for="email" />
de nouveaux le développeur
 peut utiliser des bibliothèques de composants
Si erreur de validation,  créer ses propres composants
le message est affiché ici

R. Grin JSF page 25 R. Grin JSF page 26

En-tête
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0
Transitional//EN"

Exemple plus complet de page JSF


"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>Inscription</title>
<h:outputStylesheet name="css/styles.css"/>
</h:head>
Fichier CSS

R. Grin JSF page 27 R. Grin JSF page 28

Liste déroulante Liste déroulante


Présentation avec 3 colonnes :
<h:body> <h:body> Le choix de l’utilisateur sera
intitulé, zone de saisie,
<h3>Inscription</h3> <h3>Inscription</h3> rangé dans cette propriété
message d’erreur éventuel
<h:form> <h:form> du backing bean
<h:panelGrid columns="3" Définis dans fichier CSS <h:panelGrid columns="3"
columnClasses="rightalign,leftalign,leftalign"> columnClasses="rightalign,leftalign,leftalign">
<h:outputLabel value="Titre : " for="titre"/> <h:outputLabel value="Titre : " for="titre"/>
<h:selectOneMenu id="titre" label="Titre" <h:selectOneMenu id="titre" label="Titre"
value="#{inscriptionBean.titre}" > value="#{inscriptionBean.titre}" >
Pour choix <f:selectItem itemLabel="" itemValue=""/> <f:selectItem itemLabel="" itemValue=""/>
d’un item <f:selectItem itemLabel="M." itemValue="M"/> Les items <f:selectItem itemLabel="M." itemValue="M"/>
<f:selectItem itemLabel="Mme" itemValue="Mme"/> <f:selectItem itemLabel="Mme" itemValue="Mme"/>
</h:selectOneMenu> </h:selectOneMenu>
<h:message for="titre"/> <h:message for="titre"/>

R. Grin JSF page 29 R. Grin JSF page 30

5
Nom et email (avec validateur) Bouton de commande
<h:outputLabel value="Nom :" for="nom"/> <h:panelGroup/>
<h:inputText id="nom" label="Nom" Bouton pour <h:commandButton id="enregistrement"
Saisie required="true" soumettre value="Enregistrer"
du nom value="#{inscriptionBean.nom}" /> formulaire action="#{inscriptionBean.confirmer}" />
<h:message for="nom" /> Message si nom pas saisi </h:panelGrid>
<h:outputLabel value="Email :" for="email"/> </h:form>
<h:inputText id="email" label="Email" </h:body> Méthode du bean lancée
required="true" </html> au clic du bouton
Saisie value="#{inscriptionBean.email}">
email <f:validator validatorId="validateurEmail"/>
</h:inputText>
<h:message for="email" /> Validateur spécial pour email,
écrit dans l’application
R. Grin JSF page 31 R. Grin JSF page 32

CSS et JSF Commentaires


 Utiliser <h:outputStylesheet> et pas <link  Par défaut les commentaires HTML (<!-- -->)
rel="stylesheet"> ne fonctionnent pas pour JSF, mais on peut
 Pour donner du style à un composant JSF, utiliser configurer web.xml pour qu’ils soient pris en
l’attribut styleClass (ou à la rigueur l’attribut compte
style pour du CSS intégré) ou un des attributs  On peut entourer les composants que l’on veut
particuliers à un composant (comme enlever temporairement par le tag <ui:remove>
columnClasses de <h:panelGrid>)
 Attention, l’id d’un composant JSF ne correspond
pas à l’id du composant HTML envoyé au client

R. Grin JSF page 33 R. Grin JSF page 34

Backing bean
 Backing bean géré par CDI, annoté par @Named
Ne pas utiliser les beans gérés par JSF, annotés
Backing bean

par @ManagedBean
et portée (scope)  Doit avoir un constructeur sans paramètre
 Utiliser les ressources injectées dans une
méthode annotée par @PostConstruct, pas
dans un constructeur (il existe aussi
@PreDestroy pour « faire le ménage » avant la
suppression du bean)

R. Grin JSF page 35 R. Grin JSF page 36

6
Exemples Bean géré
 Ce bean sera désigné par employeBean :  Quand une page JSF désigne un bean, par
@Named exemple #{inscriptionBean.nom} le
@RequestScoped container CDI regarde s’il existe un bean du
public class EmployeBean { … }
même type dans la portée
 Celui-ci sera désigné par employe :
 Si c’est le cas, le bean existant est utilisé par la
@Named("employe")
@RequestScoped page JSF
public class EmployeBean { … }  Sinon un nouveau bean est créé par le container
et utilisé par la page JSF

R. Grin JSF page 37 R. Grin JSF page 38

getter Exemple
@Named
 Quand une page JSF utilise une propriété d’un @ViewScoped
backing bean, le getter de la propriété peut être public class CustomerMBean implements Serializable {
appelé plusieurs fois private List<Customer> customerList;

 Un getter ne doit donc pas comporter de code


@EJB
private CustomerManager customerManager;
coûteux, tel qu’un accès à une base de données public List<Customer> getCustomers() {
if (customerList == null) {
customerList = customerManager.getAllCustomers();
}
return customerList;
}
}
R. Grin JSF page 39 R. Grin JSF page 40

Utilité de la portée Portées CDI


 Dependent : (portée par défaut) nouvel objet créé à
 Détermine la durée de vie du backing bean
chaque référence ; fin quand le but de l’injection est
 Si la portée est la session, il va durer jusqu’à la fin supprimé
de la session de travail de l’utilisateur ; si une autre
 RequestScoped : requête HTTP
expression EL y fait référence pendant la même
 ViewScoped : associé à une vue (page JSF)
session, c’est le même bean qui sera utilisé
 FlowScoped : associé à un ensemble de vues
 On peut ainsi conserver des informations entre 2
requêtes, 2 pages ou, plus généralement 2  ConversationScoped : début et fin codé en Java
traitements  SessionScoped : session de travail de l’utilisateur
 Le développeur doit choisir la bonne portée pour ne  ApplicationScoped : durée de vie de l’application ;
pas encombrer la mémoire du serveur ressources partagées entre tous les utilisateurs
R. Grin JSF page 41 R. Grin JSF page 42

7
Serializable 2 types de portée
 Les backing beans qui ont une autre portée que  JSF a aussi ses portées mais il faut utiliser les
RequestScoped doivent implémenter portées CDI (Contexts and Dependency Injection)
Serializable car ils peuvent être retirés  Attention à importer le bon paquetage
temporairement de la mémoire centrale par le  Les annotations CDI sont dans le paquetage
container javax.entreprise.context
sauf javax.faces.view.ViewScoped
 Ne jamais utiliser javax.faces.bean pour CDI !

R. Grin JSF page 43 R. Grin JSF page 44

 Indispensable de bien comprendre tout le


processus qui se déroule entre

Le cycle de vie
 la soumission d’un formulaire par l’utilisateur

 la réponse du serveur sous la forme d’une

page HTML

R. Grin JSF page 45 R. Grin JSF page 46

Simple demande page HTML


Le cycle de vie JSF

 Étudions tout d’abord le cas simple d’une requête


HTTP GET d’un client qui demande une page JSF,
sans passer de paramètres dans la requête

R. Grin JSF page 47 R. Grin JSF page 48

8
Restauration de la vue Restauration de la vue
et rendu de la réponse et rendu de la réponse
 Sur le serveur la vue qui représente la page  Sur le serveur la vue qui représente la page
demandée est construite (ou restaurée si elle avait demandée est construite (ou restaurée si elle avait
déjà été affichée) : phase « Restore View » déjà été affichée) : phase « Restore View »
 Il n’y a pas de données venant du client à traiter
donc la vue est immédiatement codée en HTML et
envoyée au client dans la phase « Render
Response »

R. Grin JSF page 49 R. Grin JSF page 50

Traitement d’un formulaire


 Nous allons cette fois-ci partir d’une requête HTTP
POST générée par la soumission d’un formulaire
d’une page JSF
 L’utilisateur a saisi des valeurs dans ce formulaire
qui est sur le poste client
 Ces valeurs sont passées comme des paramètres
de la requête qui est envoyée au serveur Web
 Tout le cycle de vie se déroule sur le serveur

R. Grin JSF page 51 R. Grin JSF page 52

Phase de restauration de la vue


 La vue qui correspond à la page qui contient le
formulaire est restaurée (phase « Restore View »)

R. Grin JSF page 53 R. Grin JSF page 54

9
Phase d’application des paramètres
 Les paramètres de la requêtes HTTP (valeurs
saisies par l’utilisateur dans le formulaire) sont
attribués aux composants Java de la vue : phase
« Apply Request Values »
 Par exemple, si l’utilisateur a saisi un nom dans
un composant texte du formulaire HTML, le
composant Java associé conserve ce nom dans
une variable interne

R. Grin JSF page 55 R. Grin JSF page 56

Phase de validation Validation sur le client HTTP


 Les expressions EL sont utilisées pour avoir les  Possible de valider avec une fonction JavaScript
propriétés Java correspondant aux valeurs par une balise JSF personnalisée Intérêt ?
récupérées à l’étape précédente :
 Il est plus simple de s’appuyer sur une
<h:inputText value="#{bean.nom}" ... />
bibliothèque de composants JSF
 Les données sont converties dans le type Java et
 Attention, ne remplace pas une validation sur le
ensuite validées pour voir si elles respectent les
contraintes indiquées dans la page JSF et le bean serveur Pourquoi?

 Si une conversion ou validation échoue, la phase


suivante sera la phase « Rendu de la réponse »
(après génération de tous les messages d’erreur)
R. Grin JSF page 57 R. Grin JSF page 58

Phase de mise à jour du modèle


 Toutes les données ont été validées ; elles peuvent
être mises dans les propriétés des backing beans
associées (expressions EL) :
<h:inputText id="nom"
value="#{bean.utilisateur.nom}" />

R. Grin JSF page 59 R. Grin JSF page 60

10
Phase d’invocation de l’application
 Maintenant que les données sont enregistrées
dans les backing beans l’action associée au
bouton ou au lien cliqué par l’utilisateur peut être
lancée (une méthode des backing beans est
exécutée)
 La valeur de retour de cette méthode détermine
la prochaine page à afficher (navigation)

R. Grin JSF page 61 R. Grin JSF page 62

Phase de rendu de la réponse


 La page déterminée par la valeur retour de la
méthode « action » est codée en HTML et
envoyée vers le client HTTP

R. Grin JSF page 63 R. Grin JSF page 64

Cycle de vie avec une requête GET Sauter des phases


 Il est quelquefois indispensable de sauter des
 Si l’URL de la requête contient des paramètres phases du cycle de vie
http://serveur/appli/adresse?p1=v1&p2=v2
et si la page JSF demandée contient des
paramètres de vue (étudiés plus loin) pour ranger
ces valeurs dans un backing bean,
le cycle de vie complet est exécuté comme avec
une méthode POST

R. Grin JSF page 65 R. Grin JSF page 66

11
immediate="true" immediate="true"
sur un UICommand sur un EditableValueHolder
 Attribut de bouton ou de lien  Attribut de champ de saisie (<h:inputText>,…)
(<h:commandButton>, <h:commandLink>)  Déclenche le traitement de la valeur saisie, avant
 Les valeurs des composants « UIInput » tels que les autres valeurs saisies
<h:inputText> ne sont pas traités, sauf ceux qui  Pour effectuer des modifications sur l’interface
ont aussi l’attribut immediate="true" utilisateur sans valider toutes les valeurs du
 Utilité : bouton d’annulation d’un formulaire ; on ne formulaire
veut pas traiter les champs de saisie du formulaire

R. Grin JSF page 67 R. Grin JSF page 68

Exemple (1/2) Exemple (2/2)


 Formulaire qui contient un champ de saisie du
 La solution : « immediate=true » sur le champ
code postal et un champ de saisie de la ville
code postal
 Lorsque le code postal est saisi, un écouteur met
 et, dans la méthode ecouteur,
automatiquement à jour la ville :
<h:inputText  mettre à jour la ville,
valueChangeListener="#{bean.ecouteur}"  ajouter ce code à la fin (pour éviter la validation
value = "#{bean.codePostal}"
onChange = "this.form.submit()" />
des autres champs) :
FacesContext context =
 La soumission du formulaire ne doit pas lancer la FacesContext.getCurrentInstance();
validation de tous les composants du formulaire context.renderResponse();
qui ne sont peut-être pas encore saisis
R. Grin JSF page 69 R. Grin JSF page 70

Exemple de web.xml (1/2)


<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"

Fichiers de configuration
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
Production
pour livrer au client

R. Grin JSF page 71 R. Grin JSF page 72

12
Exemple de web.xml (2/2) Phase de projet
<session-config>  Attention, la phase de projet Development
<session-timeout>30</session-timeout> ajoute automatiquement l’affichage des
messages d’erreurs JSF même s’il n’y a aucun
</session-config>
<welcome-file-list>
<welcome-file>faces/index.xhtml</welcome-file> composant pour les messages dans la page
</welcome-file-list> (<h:message> ou <h:messages>)
</web-app> Pas de « / » avant faces !  Ne pas oublier de mettre ces composants dans la
page pour que l’utilisateur voit les messages
quand l’application sera en Production

R. Grin JSF page 73 R. Grin JSF page 74

faces-config beans.xml
 WEB-INF/faces-config.xml : configuration de la  Indispensable pour utiliser CDI dans Java EE 6
partie JSF de l’application, par exemple pour mais plus dans Java EE 7
indiquer un « bundle » pour l’internationalisation  Sous WEB-INF si fichier WAR ou sous META-INF
ou pour donner des règles de navigation sinon

R. Grin JSF JSF - page 75 R. Grin JSF page 76

Navigation dans JSF


 Navigation = affichage d’une nouvelle page
 Composants JSF standards pour la navigation :

Navigation
<h:commandButton>, <h:button>,
<h:commandLink>, <h:link>, <h:ouputLink>
 <h:commandButton> et <h:commandLink>
doivent être dans une balise <h:form> ; ils
soumettent le formulaire
 Les autres composants peuvent ne pas être dans un
formulaire et, s’ils le sont, ils ne le soumettent pas

R. Grin JSF page 77 R. Grin JSF page 78

13
POST ou GET Recommandation
 <h:commandButton> et <h:commandLink>  Requête GET si on veut juste afficher une page ;
soumettent le formulaire par une requête POST et par exemple afficher la liste de tous les clients
affichent la page indiquée par l’attribut action  Requête POST si l’utilisateur a saisi des données
 <h:button> et <h:link> lancent une requête qu’on veut enregistrer sur le serveur ; par
GET et affichent la page indiquée par l’attribut exemple si l’utilisateur a modifié le nom d’un
outcome client

R. Grin JSF page 79 R. Grin JSF page 80

Adresse affichée par le navigateur Exemple


 Avec une requête POST le navigateur affiche <h:form> Dans formulaire.xhtml
l’URL du formulaire qui vient d’être soumis, alors ...
que la nouvelle page est affichée ; <h:commandButton
si on veut voir le bon URL, il faut ajouter une value="Envoyer la commande"
redirection action="#{commandeBean.checkout()}" />
 Avec <h:button> et <h:link> le navigateur
</h:form>
affiche le bon URL
Si la méthode checkout retourne "page2",
page2.xhtml sera affichée, mais le navigateur
affichera toujours l’URL de formulaire.xhtml

R. Grin JSF page 81 R. Grin JSF page 82

Navigation statique et dynamique Exemple de navigation statique


 La valeur qui détermine la nouvelle page peut être  Dans page1.xhtml :
<h:commandButton
 définie « en dur » au moment de l’écriture de
action="page2"
l’application ; la navigation est statique value="Aller à page2" />
 définie par la valeur retournée par une méthode
Page suivante si clic sur bouton ? page2.xhtml
Java ; la navigation est dynamique

R. Grin JSF page 83 R. Grin JSF page 84

14
Exemple de navigation dynamique Navigation déclarative ou implicite
 Dans page1.xhtml :  Si la valeur qui détermine la nouvelle page
<h:commandButton
correspond à une règle de navigation, la règle
action="#{bean.faire()}"
value="Faire …" /> détermine la prochaine page à afficher ; c’est la
navigation déclarative
 Dans le backing bean : Page suivante
@Named si clic sur bouton ?  Sinon c’est la navigation implicite (ce qu’on a vu
@RequestScoped jusqu’à maintenant)
public class Bean { page2.xhtml ou
public String faire(){ autrePage.xhtml
...
if (...) return "page2";
else return "autrePage";
}
R. Grin JSF page 85 R. Grin JSF page 86

Exemple avec règle de navigation Exemple sans règle de navigation


 Règle de navigation dans faces-config.xml :
<navigation-rule>
<from-view-id>page1.xhtml</from-view-id>
<navigation-case>
<from-outcome>ok</from-outcome> Pas de règle de navigation
<to-view-id>/succes.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
 Dans page1.xhtml :  Dans page1.xhtml :
<h:commandButton action="ok" <h:commandButton action="ok"
value="Valider" /> value="Valider" />

Page suivante si clic sur /succes.xhtml Page suivante si clic sur ok.xhtml
bouton ?
R. Grin JSF page 87
bouton ?
R. Grin JSF page 88

Redirection
 Dans le cas d’une navigation statique, ajouter
?faces-redirect=true à l’URL de la page à

PRG
afficher :
action="page2?faces-redirect=true"
 Dans le cas d’une navigation dynamique, ajouter
cette même chaîne à la valeur retournée par la
méthode :
return "page2?faces-redirect=true";

R. Grin JSF page 89 R. Grin JSF page 90

15
Problèmes avec POST La raison des problèmes
 Un formulaire est dans une page F ; la soumission  C’est JSF qui dirige vers la nouvelle page (à la fin
du formulaire par une requête POST affiche une de la phase « Invoke Application »)
page P en réponse
 Le navigateur n’en a pas connaissance et pense
 2 problèmes : être toujours dans la page qui contient le formulaire
 l’adresse de P affichée par le navigateur est
l’adresse de F
 un refresh de F (ou un retour en arrière) après le
POST soumet à nouveau le formulaire

R. Grin JSF page 91 R. Grin JSF page 92

Les conséquences du problème La solution :


 Impossible de garder un marque-page pour la
POST, REDIRECT, GET (PRG)
page affichée après un POST
 Le modèle POST– REDIRECT– GET :
 Commandes ou paiements involontaires dus à
 Ne jamais montrer une page en réponse à un
des soumissions multiples d’un formulaire
POST
(demande de confirmation, mais par une fenêtre
 Charger les pages uniquement avec des GET
pop-up difficile à comprendre et/ou stressante
pour l’utilisateur)  Utiliser la redirection pour passer de POST à
GET

R. Grin JSF page 93 R. Grin JSF page 94

Sans PRG Avec PRG


Wikipedia

R. Grin JSF page 95 R. Grin JSF page 96

16
Problèmes de PRG Portée session ?
 La redirection peut poser un problème :  Une des solutions est de ranger les informations
 les informations conservées dans la requête du dans la session plutôt que dans la requête
POST ne sont plus disponibles après la  Mais cette solution peut conduire à une session
redirection trop encombrée ou à d’autres problèmes
 les messages de succès ou d’information,  Une meilleure solution est de passer les
générés par programmation ne sont conservés informations d’une requête à l’autre
que le temps d’une requête et donc
n’apparaissent plus après une redirection

R. Grin JSF page 97 R. Grin JSF page 98

Solutions Paramètre de vue


 JSF 2 offre 3 possibilités pour conserver les  Une requête GET avec 2 paramètres correspond à
informations plus longtemps que la requête : un URL de ce type :
 les paramètres de vue (permet de rester en http://serveur.fr/appli/page2.xhtml?nom=toto&age=23
portée « requête »)  Un paramètre de vue est utilisé avec une requête
 les portées « conversation » ou « flot » de CDI GET (après une redirection par exemple) pour ranger
 la classe Flash (peu utilisé) un paramètre de la requête dans une propriété d’un
 Si on veut réafficher le même formulaire, on peut backing bean
aussi utiliser la portée « vue »  Représenté par une balise <f:viewParam> de la
page demandée par GET (page2.xhtml pour cet
exemple)
R. Grin JSF page 99 R. Grin JSF page 100

Exemple <f:viewAction>
 Dans page2.xhtml (juste avant <h:head>) :  Balise d’une page demandée par GET
<f:metadata>
 Exécute une méthode d’un backing bean
<f:viewParam name="nom"
value="#{bean.p1}" />  Exemple :
</f:metadata> <f:viewAction action="#{bean.m()}"/>
 Requête GET  Cette méthode est exécutée
page2.xhtml?nom=Bob  après l’enregistrement des valeurs des
Bob sera mis automatiquement dans la propriété paramètres de la requête GET par un
p1 de bean, avant l’affichage de la page <f:viewParam>
 On peut ajouter un convertisseur et un validateur,  avant l’affichage de la page qui contient cette
comme avec <h:inputText> balise
R. Grin JSF page 101 R. Grin JSF page 102

17
Exemple d’utilisation Code de l’exemple
 Soit une requête GET http://…/emp.xhtml?id=12
 Dans la page JSF :
 Un paramètre de vue de la page emp.xhtml récupère <f:metadata>
la valeur de l’id de la requête (12) dans la propriété <f:viewParam name="id" value="#{bean.id}"/>
id d’un backing bean <f:viewAction action="#{bean.getEmp()}"/>
 L’action de <f:viewAction> récupère dans la base
</f:metadata>
de données l’employé qui a l’id 12 et le met dans une  Dans le backing bean (id et emp sont des
propriété emp de type Employe du backing bean propriétés du bean) :
public void getEmp() {
 Ces données sont ensuite affichées dans la page
this.emp = ejb.getEmp(this.id);
emp.xhtml qui contient, par exemple, }
#{bean.emp.nom}

R. Grin JSF page 103 R. Grin JSF page 104

includeViewParams Utilité de includeViewParams


 Cet attribut peut être ajouté à tout lien vers une page  Si 2 pages utilisent des backing beans de portée
qui contient des paramètres de vue : requête de la même classe, ça permet de passer des
<h:commandButton value=… valeurs du backing bean d’une page dans le backing
action="page2?faces-redirect=true bean de l’autre page, malgré une redirection
&amp;includeViewParams=true" />
 Le 1er bean qui ne va durer que le temps de la
 includeViewParams signifie « inclure dans le lien requête POST va passer dans la requête GET de
les paramètres qui correspondent aux paramètres de redirection toutes les valeurs attendues par la page
vue de la page de destination, avec les valeurs d’arrivée, à partir des valeurs qu’il a dans ses
définies par l’expression EL du paramètre de vue » propriétés
 Le 2ème bean utilisé par la page d’arrivée mettra ces
valeurs dans ses propriétés
R. Grin JSF page 105 R. Grin JSF page 106

Exemple includeViewParams avec GET


2 instances différentes de Bean
 page2.xhtml :
<f:viewParam name="n1"  Avec un composant « GET » comme <h:link>,
value="#{bean.p1}" /> il faut utiliser l’attribut includeViewParams :
 Lien de page1.xhtml : <h:link outcome="page"
<h:commandButton value=… includeViewParams="true" ... >
action="page2?faces-redirect=true
&amp;includeViewParams=true" />
 Supposons que p1 = 5 au départ de la requête,
alors la requête GET aura le paramètre
« n1="#{bean.p1}" » donc « n1=5 »
 En arrivant dans page2.xhtml, la valeur 5 sera
donc mise dans la propriété bean.p1
R. Grin JSF page 107 R. Grin JSF page 108

18
Donner une valeur à un paramètre (1/2) Donner une valeur à un paramètre (2/2)
 Plusieurs façons de donner une valeur à un  Dans le cas d’une requête POST avec redirection,
paramètre de requête GET : écrire une méthode action dont la valeur de retour
 <h:link outcome="page?p1=4&amp;p2='bibi'" contient la valeur du paramètre :
public String faire() {
… >
return "page?nb="+ nombre
<h:link outcome="page?p1=#{bean.prop + 2}"
+ "&amp;faces-redirect=true";
… >
}
 <h:link outcome="page" …>
<f:param name="p1" value="4"/>
</h:link>

R. Grin JSF page 109 R. Grin JSF page 110

Cas d’utilisation
 Les conversions et validations peuvent conduire à
l’affichage de messages d’erreur

Messages d’erreur ou d’information


 Lorsqu’une action s’est bien déroulée il faut le
signaler à l’utilisateur par un message
d’information / de succès
 On peut vouloir informer l’utilisateur pendant le
déroulement d’une action

R. Grin JSF page 111 R. Grin JSF page 112

Affichage des messages Format d’affichage des messages


 <h:messages> indique un emplacement de la  Les attributs styleClass et style indiquent un
page où sont affichés tous les messages style CSS pour affichage des messages
 <h:message> indique un emplacement pour  D’autres attributs définissent un style lié à la
afficher un message pour un seul composant sévérité du message (errorStyle,
identifié par l’attribut for errorClass, infoStyle, infoClass,…)
 Ces 2 balises ont de nombreux attributs

R. Grin JSF page 113 R. Grin JSF page 114

19
Exemples Messages standards
 Messages de sévérité « ERROR » affichés avec la  Les convertisseurs et validateurs standards
classe CSS « erreur » et messages de sévérité produisent des messages d’erreur standards
« INFO » affichés en vert et caractères gras  Les attributs des composants standards
<h:messages errorClass="erreur" requiredMessage, converterMessage ou
infoStyle="color:green; font-weight:bold;"/>
validatorMessage permettent de modifier ces
<h:messages globalOnly="true"/>
messages

 <h:message for="nom" />
 Exemple :
 <h:message for="nom" showSummary="true"
<h:inputText id="nom" required="true"
showDetail="false" />
requiredMessage="Le nom est requis"/>

R. Grin JSF page 115 R. Grin JSF page 116

Améliorer les messages standards Messages personnalisés


 Un message d’erreur peut comporter l’identifiant  Les convertisseurs et validateurs personnalisés
du composant concerné par l’erreur permettent aussi d’afficher facilement des
 Pour rendre les messages plus clairs, il faut messages d’erreur
donner des identifiants significatifs (attribut id)  S’ils ne peuvent être utilisés (par exemple
aux formulaires et aux composants pour éviter les validation qui englobe plusieurs composants) ou
« j_idt6:j_idt8 » dans les messages pour afficher un message de succès, le
 On peut aussi utiliser l’attribut label des développeur peut ajouter ses messages dans les
composants ; ce label sera utilisé pour désigner pages JSF par programmation Java
le composant dans les messages

R. Grin JSF page 117 R. Grin JSF page 118

Classe FacesMessage Faire afficher son propre message


 La classe FacesMessage du paquetage  Il suffit de passer une instance de FacesMessage
javax.faces.application représente un à la méthode addMessage de FacesContext
message affiché par <h:messages> ou pour qu’il soit affiché si la page affichée ensuite
<h:message> contient <h:messages> ou <h:message>
 Propriétés d’un messages :  addMessage a 2 paramètres :
 sévérité (SEVERITY_FATAL, SEVERITY_ERROR,  id client du composant avec lequel le message
SEVERITY_WARN, SEVERITY_INFO de la classe interne est associé (de type String ; null si le
Severity)
message n’est pas attaché à un client particulier)
message détaillé
 Le message (classe FacesMessage)

 message résumé

R. Grin JSF page 119 R. Grin JSF page 120

20
Exemple Message de validation ou conversion
String msgResume = "...";  Quand il y a une erreur dans un convertisseur ou un
String msgDetail = "..."; validateur, le code doit lancer une exception
FacesMessage.Severity niveau =
(ConverterException ou ValidatorException)
FacesMessage.SEVERITY_ERROR;
FacesMessage fm = et c’est le message passé au constructeur de
new FacesMessage(niveau, l’exception qui est affiché
msgResume, msgDetail);  Pas besoin de préciser le composant associé au
FacesContext.getCurrentInstance()
message puisque c’est celui qui est validé
.addMessage(null, fm);

Message pas lié à


un composant
R. Grin JSF page 121 R. Grin JSF page 122

Exemple de message dans validateur Id client d’un composant


public void validateId(FacesContext context,  Si on veut associer un message à un composant
UIComponent composant, Object id) { et qu’on n’est pas dans un convertisseur ou un
Compte compte = ejb.getCompte((Long) id); validateur il faut trouver l’id client du composant
if (compte == null) {
 Exemple de désignation d’un composant : si le
FacesMessage message =
composant a l’id « montant » dans le formulaire
new FacesMessage("id n’existe pas");
d’id « transfert », l’id du composant à passer
throw new ValidatorException(message);
en 1er paramètre de addMessage est
}
« transfert:montant »
}

R. Grin JSF page 123 R. Grin JSF page 124

Identificateur « client » Message après redirection


 Pas toujours facile de trouver les id « client »  Puisqu’un message ne dure qu’une seule
 Regarder le code HTML généré aide requête, il ne s’affichera pas après une
 Identificateur client composé de plusieurs niveaux, redirection
avec le séparateur « : » ; par exemple,  Si on veut qu’il s’affiche, il faut un code un peu
« formulaire:j_idt41 » plus complexe qui utilise la classe Flash
 Pour faciliter, donner des ids aux composants (y
compris composants englobants)
 Identificateur doit exister dans la page HTML générée
(attention aux rendered="false")

R. Grin JSF page 125 R. Grin JSF page 126

21
Ajouter un message « flash »
void addFlashMessage(FacesMessage message) {
FacesContext facesContext =
FacesContext.getCurrentInstance();
Flash flash =
facesContext.getExternalContext()
Événements
.getFlash();
flash.setKeepMessages(true);
facesContext.addMessage(null, message);
}

R. Grin JSF page 127 R. Grin JSF page 128

Types d’événements Événements « application »


 2 grands types d’événements :  ValueChangeEvent : valeur d’un composant
 Généré par une action de l’utilisateur modifiée par l’utilisateur (champ de saisie de
(de type « application ») texte, menu, liste déroulante,…) ; liés aux
composants tels que <h:selectOneMenu> ou
 Généré à un moment du cycle de vie
<h:inputText>
(de type « lifecycle »)
 ActionEvent : clic sur un bouton ou un lien ; liés
aux composants tels que <h:commandButton>
ou <h:commandLink>

R. Grin JSF page 129 R. Grin JSF page 130

Écouteur Exemples
 Les événements sont traités par des écouteurs,  Classe écouteur :
 méthodes (attribut xxxListener)
<h:selectOneMenu value="#{bean.codePays}"
onchange="submit()">
 ou classe (sous-balise <f:xxxListener>)
<f:valueChangeListener
type="fr.unice.jsf.PaysListener" />
<f:selectItems value="#{bean.listePays}" />
</h:selectOneMenu>
 Méthode écouteur :
<h:selectOneMenu value="#{bean.codePays}"
onchange="submit()"
valueChangeListener="#{bean.modifPays}" />
<f:selectItems value="#{bean.listePays}" />
</h:selectOneMenu>

R. Grin JSF page 131 R. Grin JSF page 132

22
Méthode valueChangeListener
 ValueChangeEvent en paramètre et void en retour
 Exemple :
Ajax
public void modifPays(ValueChangeEvent evt) {
for (Locale locale : pays) {
if (locale.getCountry().equals(evt.getNewValue())) {
FacesContext.getCurrentInstance()
.getViewRoot().setLocale(locale);
}
}

Que fait cette méthode ?

R. Grin JSF page 133 R. Grin JSF page 134

Ajax <f:ajax>
 Asynchronous Javascript and XML  Ajoute des fonctionnalités « Ajax » au composant
 Une requête Ajax envoyée par le client est lancée JSF dans lequel il est inclus
« en arrière-plan » ; l’utilisateur peut continuer à  Un événement déclencheur sur ce composant
travailler avec la page en cours provoque l’envoi d’une requête Ajax au serveur
 La réponse du serveur à la requête ne modifie sans attendre la soumission du formulaire
qu’une partie de la page en cours  Par exemple, une liste déroulante peut envoyer
une requête Ajax quand l’utilisateur fait un
nouveau choix ; la réponse du serveur ne mettra
à jour qu’une partie de la page

R. Grin JSF page 135 R. Grin JSF page 136

<f:ajax> - les attributs Exemple 1


 event : événement déclencheur de la requête ; <h:form>
valeur par défaut dépend du composant, par <h:input value=... />
exemple, valueChange pour les <h:inputText> <h:commandButton value="Enregistrer" ...>
ou action pour les <h:commandButton> <f:ajax execute="@form"
 execute : composants pris en compte pour l’envoi render="out1 out2" />
de la requête (espace séparateur) ; valeur par </h:commandButton>
défaut @this, le composant qui contient <f:ajax> <h:input id="out1" .../>
 render : composants mis à jour au moment de la <h:input id="out2" .../>
réponse du serveur (espace séparateur) ; valeur par </h:form>
défaut @none
R. Grin JSF page 137 R. Grin JSF page 138

23
Exemple 2 Identificateurs « client »
<h:form>  Utilisés dans les attributs execute et surtout render
<h:input value="#{bean.prop" />
 Revoir section « Messages d’erreur ou d’information »
<h:selectOneMenu value="#{bean.selected}">
<f:selectItems value="#{bean.items}" />
<f:ajax execute="@form"
render="@form" />
</h:selectOneMenu>
...
</h:form>

R. Grin JSF page 139 R. Grin JSF page 140

Listener Exemple – valeur composant


 L’attribut listener de <f:ajax> désigne une public void m(AjaxBehaviorEvent event) {

méthode qui est appelée quand l’événement


UIInput input = (UIInput) event.getComponent();
UnType valeur = (UnType) input.getValue();
déclencheur de l’appel Ajax survient : ...
<f:ajax event="keyup" render="table" }
listener="#{bb.m}"
 Exemples : un écouteur peut récupèrer
La méthode « listener » doit avoir un
 un client à partir d’un id saisi par l’utilisateur paramètre de type AjaxBehaviorEvent
 tous les noms qui commencent par les caractères

tapés par l’utilisateur (complétion de nom)

R. Grin JSF page 141 R. Grin JSF page 142

 JSF 2.2 supporte HTML5

JSF et HTML5

R. Grin JSF page 143 R. Grin JSF page 144

24
Pages HTML5 Composants HTML5
 Les pages HTML générées par JSF sont  Avant JSF 2.2, pour profiter d’un nouveau
maintenant déclarées comme des documents composant HTML dans JSF il fallait écrire un
HTML5 avec composant JSF
<DOCTYPE html>  JSF 2.2 permet de profiter des services offerts
par JSF (validation de données, affichage
automatique des messages d’erreur, utilisation
simple d’Ajax, …) tout en utilisant un attribut ou
un composant HTML5

R. Grin JSF page 145 R. Grin JSF page 146

2 possibilités Attributs HTML5 en JSF


 Avec un composant JSF, on peut utiliser un attribut  Etudions d’abord le cas d’un attribut HTML5 dans
HTML5 (attribut « pass-through ») dans un un composant JSF
composant JSF
 Exemple : attribut placeholder de <input>
alors qu’il n’est pas un attribut de <h:inputText>
 L’attribut sera passé tel quel à la balise HTML
générée à la fin du cycle de vie JSF
 On peut aussi utiliser une balise HTML5, tout en
profitant des facilités de JSF avec des attributs
traités par JSF
R. Grin JSF page 147 R. Grin JSF page 148

Passer un attribut HTML5 Balise HTML5 en JSF


1. Avec l’espace de noms « passthrough » :  Etudions maintenant comment insérer une balise
<html ... HTML5 dans une page JSF
xmlns:p="http://xmlns.jcp.org/jsf/passthrough">
<h:form>
<h:inputText value="#{bean.nom}"
p:placeholder="Votre nom"/>
...
2. Avec une balise <f:passThroughAttribute> :
<h:inputText value="#{bean.nom}" >
<f:passThroughAttribute
name="placeholder" value="Votre nom" />
</h:inputText>
R. Grin JSF page 149 R. Grin JSF page 150

25
Composants HTML5 pass-through Exemple
<!DOCTYPE html>
 Une page JSF peut contenir des balises HTML5
<html xmlns="http://www.w3.org/1999/xhtml"
 Pour qu’un élément HTML5 soit traité par JSF il
xmlns:jsf="http://xmlns.jcp.org/jsf">
suffit qu’un de ses attributs ait l’espace de noms <form jsf:id="form">
http://xmlns.jcp.org/jsf :
<input name="nom" type="text"
<html xmlns:jsf="http://xmlns.jcp.org/jsf">
jsf:id="nom" value="#{bean.nom}"/>
...
<input type="text" jsf:id="nom" <input type="submit" value="Soumettre"
placeholder="Votre nom" jsf:id="submitButton" />
value="#{bean.name}"/> <p>Texte saisi : #{bean.text}</p>
</form>
la valeur de value sera associée
</html> à la propriété nom du backing bean
R. Grin JSF page 151 R. Grin JSF page 152

Composant HTML5 traité par JSF Composant HTML5 sans équivalent JSF
 Le composant HTML5 est transformé en un  Si le composant HTML5 n’a pas de correspondance
composant JSF suivant un mapping qui tient compte dans JSF, JSF crée un composant spécial de balise
du nom de la balise et éventuellement de certains <jsf:element>
attributs de la balise  On peut ajouter des capacités Ajax à un tel composant
 Exemple : <input> est transformé en (voir exemple à suivre), mais seulement quelques
<h:inputFile> si elle a l’attribut type="file" attributs JSF liés aux événements JavaScript
 Les valeurs des attributs HTML5 peuvent contenir des
expressions EL comme pour les composants JSF

R. Grin JSF page 153 R. Grin JSF page 154

Exemple Exemple avec <input type="date">


 Dans cet exemple adapté de Michael Kurz, on <h:form>
ajoute un comportement Ajax à la balise HTML div : <h:outputLabel value="Date : " for="date" />
<input type="date" jsf:id="date" name="date"
<div jsf:id="clickCounter">
Clics: #{bean.compterClicsSouris} value="#{reservationBean.date}">
<f:ajax event="click" render="@this" <f:convertDateTime pattern="yyyy-MM-dd"/>
listener="#{bean.incrementer}"/> </input>
<br/>
</div>
<h:commandButton value="Enregistrer la date"
action="resultat"/>
</h:form>

Le type de date est java.util.Date

R. Grin JSF page 155 R. Grin JSF page 156

26
Ressources des pages Web

 Les pages HTML peuvent faire appel à des


ressources externes : images, fichier CSS, code
Ressources Javascript,…
 Il faut utiliser des balises JSF dédiées aux
ressources à la place des balises HTML ; en ce cas
JSF va chercher les ressources dans le répertoire
resources placé sous la racine des pages Web

R. Grin JSF page 157 R. Grin JSF page 158

CSS JavaScript
 Le plus souvent on met les fichiers CSS dans un  Le plus souvent on met les fichiers JavaScript
sous-répertoire css du répertoire resources dans un sous-répertoire js du répertoire
<h:outputStylesheet name="css/style.css" /> resources
<h:outputScript name="js/jsf.js" />
 Par défaut, le script sera placé dans la page
HTML à l’endroit où on a mis la balise ; on peut
ajouter un attribut pour indiquer où le mettre (par
exemple target="head")

R. Grin JSF page 159 R. Grin JSF page 160

Image Thèmes
<h:graphicImage name="images/photo.jpg" />  Depuis JSF 2.2 on peut avoir des thèmes qui
réunissent les ressources en utilisant l’attribut
library des balises que l’on vient de voir ; pas
étudié ici

R. Grin JSF page 161 R. Grin JSF page 162

27

Vous aimerez peut-être aussi