3 JSF
3 JSF
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
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
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
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
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
En-tête
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0
Transitional//EN"
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
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)
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
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;
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 !
Le cycle de vie
la soumission d’un formulaire par l’utilisateur
page HTML
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 »
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
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)
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
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
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
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
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
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
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
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";
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
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
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}
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&p2='bibi'" contient la valeur du paramètre :
public String faire() {
… >
return "page?nb="+ nombre
<h:link outcome="page?p1=#{bean.prop + 2}"
+ "&faces-redirect=true";
… >
}
<h:link outcome="page" …>
<f:param name="p1" value="4"/>
</h:link>
Cas d’utilisation
Les conversions et validations peuvent conduire à
l’affichage de messages d’erreur
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"/>
message résumé
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);
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);
}
É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>
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);
}
}
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
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>
JSF et HTML5
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
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
26
Ressources des pages Web
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")
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
27