Projets
Pages enfant
  • 1.9.3 Ecriture des formulaires

Vous regardez une version antérieure (v. /wiki/spaces/PROJ/pages/100663477/1.9.3+Ecriture+des+formulaires) de cette page.

afficher les différences afficher l'historique de la page

« Afficher la version précédente Vous regardez la version actuelle de cette page. (v. 14) afficher la version suivante »

Bon pour relecture

Sommaire :


Exemple

L'écriture des formulaires JSF ne déroutera pas l'habitué des formulaires JSP. On utilisera par exemple :

<h:form id="administratorAddForm">
  <h:messages />
  <h:outputLabel
     for="ldapUid"
     value="#{msgs['ADMINISTRATOR_ADD.TEXT.PROMPT']}" />
  <h:inputText
      id="ldapUid"
      value="#{administratorsController.ldapUid}"
      required="true" />
  <h:message for="ldapUid" />
  <h:commandButton
     value="#{msgs['ADMINISTRATOR_ADD.BUTTON.ADD_ADMIN']}"
     action="#{administratorsController.addAdmin}" />
  <h:commandButton
     value="#{msgs['_.BUTTON.CANCEL']}"
     action="cancel"
     immediate="true" />
</h:form>

L'attribut value de la balise <h:inputText> contient une référence vers un attribut du contrôleur. Cet attribut sera mis à jour lors de la validation du formulaire.L'attribut action du bouton <h:commandButton> contient une référence vers la callback (méthode) du contrôleur. C'est elle qui sera appelée lors de l'appui sur le bouton et dont le résultat sera utilisé par les règles de navigation pour connaître la page que l'application doit afficher en retour.
Le deuxième bouton a un attribut immediate="true". Dans ce cas, les attributs du contrôleur relatifs aux balises <h:inputText> ne seront pas mis à jour et les éventuelles vérifications de forme ou de contenu ne seront pas exécutées. Ceci est particulièrement utile sur un bouton d'annulation comme c'est le cas ici.

Les balises <h:messages> et <h:message> sont traitées dans un paragraphe à suivre.

Exercice : Ajouter une entrée dans la barre de navigation Afficher l'énoncéCacher l'énoncé

Ajouter une entrée Test1 dans la barre de navigation (_navigationItems.jsp) qui sera toujours affichée (pas d'attribut rendered) et dont l'action sera gotoTest1 (en dur).
Ajouter une règle de navigation (dans /properties/jsf/navigation-rules.xml) pour que la sélection de cet item envoie sur la vue /stylesheets/test1.jsp.
Tester.

 

L'envoi vers test1.jsp ne doit pas marcher tant que la vue n'a pas été créée.

Afficher la solutionCacher la solution
  1. Dans _navigationItems.jsp ajouter :  
    <h:menuItem id="test1" value="Test1" action="gotoTest1" />
    
    2. Dans navigation-rules.xml ajouter :
    <navigation-rule>
     <from-view-id>*</from-view-id>
     <navigation-case>
       <from-outcome>gotoTest1</from-outcome>
       <to-view-id>/stylesheets/test1.jsp</to-view-id>
       <redirect/>
     </navigation-case>
    </navigation-rule>
    

Exercice : Ajouter une page JSF Afficher l'énoncéCacher l'énoncé

Créer la page test1.jsp, (la créer à partir de about.jsp, en ne gardant que la barre de navigation) et tester (elle doit s'afficher).

Note : la page doit s'afficher maintenant qu'elle existe.

Afficher la solutionCacher la solution

Contenu de test1.jsp :

<%@include file="_include.jsp"%>

  <%@include file="_navigation.jsp"%>



Exercice : Créer une règle de navigation Afficher l'énoncéCacher l'énoncé

Ajouter un formulaire avec un bouton Move sur la page, dont l'action gotoWelcome envoie sur la page welcome.jsp.

Ajouter une règle de navigation dans le fichier /properties/jsf/navigation-rules.xml.

Afficher la solutionCacher la solution

1. Contenu de test1.jsp :

<%@include file="_include.jsp"%>
  <%@include file="_navigation.jsp"%>
  <h:form>
    <h:commandButton value="Move"
	action="gotoWelcome"/>
  </h:form>

2. Dans navigation-rules.xml ajouter :

<navigation-rule>
  <from-view-id>/stylesheets/test1.jsp</from-view-id>
  <navigation-case>
    <from-outcome>gotoWelcome</from-outcome>
    <to-view-id>/stylesheets/welcome.jsp</to-view-id>
    <redirect />
   </navigation-case>
</navigation-rule>
Exercice : Créer un contrôleur Afficher l'énoncéCacher l'énoncé

Ajouter une classe contrôleur Test1Controller (la créer à partir de AboutController), ajouter à cette classe une méthode callback() qui renvoie la chaîne gotoWelcome, et appeler cette méthode en réaction au bouton de la page test1.jsp.
Tester.

Ne pas oublier de déclarer le bean test1Controller dans /properties/web/controllers.xml.

Afficher la solutionCacher la solution

1. Création de la classe java Test1Controller qui extends AbstractContextAwareController :

avec une méthode callback :
/**
 * JSF callback.
 * @return A String.
 */
public String callback() {
  return "gotoWelcome";
}

2. Déclaration du bean dans le fichier controller.xml :

<bean id="test1Controller"
  class="org.esupportail.formation.web.controllers.Test1Controller"
  parent="abstractContextAwareController"
  scope="session"
  >
  <description>A bean to manage the test1 page.</description>
</bean>

3. Modification le contenu de test1.jsp:

<%@include file="_include.jsp"%>
  <%@include file="_navigation.jsp"%>
  <h:form>
    <h:commandButton value="Move"
	action="#{test1Controller.callback}"/>
  </h:form>

Messages d'erreur

Dans une page JSP les balises <h:messages> et <h:message> permettent d'afficher des messages d'erreur à l'utilisateur.La balise <h:messages> permet d'afficher l'ensembles des messages d'erreurs.La balise <h:message> permet d'afficher les messages d'erreurs relatifs à une balise <h:inputText> particulière. Elle dispose, à cet effet, d'un attribut for qui lui permet de faire le lien avec un attribut id d'une balise <h:inputText> donnée. Ce mécanisme est notamment utilisé quand l'attribut required de la balise  <h:inputText> est positionné à true ou bien quand un validateur (cf. ci-dessous) lui est associé.Il est aussi possible, dans une callback d'un contrôleur, de remonter des messages d'erreurs vers la vue. Ces messages seront généralement rendus grâce à la balise <h:messages>. Typiquement, la callback renverra null, c'est-à-dire que la page affichée à l'utilisateur restera la même (celle où s'est produite l'erreur de saisie).  Dans l'exemple suivant, on affiche un message si l'utilisateur correspondant à l'identifiant donné est déjà administrateur :

User user = getDomainService().getUser(ldapUid);
if (user.getAdmin()) {
  addErrorMessage(
    "form:uid",
    "ADMINISTRATORS.MESSAGE.USER_ALREADY_ADMINISTRATOR",
    ldapUid);
  return null;
}

Le premier paramètre de la méthode addErrorMessage vaut "uid". Le message ajouté sera alors affiché par la balise <h:message for="uid">. Il est également possible de spécifier un premier paramètre null ; le message d'erreur sera global, c'est-à-dire affiché par la balise <h:messages>.

Exercice : Afficher un message sur une page JSF Afficher l'énoncéCacher l'énoncé

Ajouter une boite de saisie (id="myInput") au dessus du bouton dans le formulaire, lier le contenu de la boite à une propriété myInput de test1Controller, et modifier la méthode callback() pour qu'elle ne renvoie vers la page d'accueil que si myInput a au moins deux caractères, sinon reste sur la vue test1.jsp en affichant le message d'erreur TEST1.MESSAGE.SHORT.

On pourra utiliser le code

addErrorMessage("form:myInput", "TEST1.MESSAGE.SHORT"))

et déclarer le message d'erreur dans les fichiers /properties/i18n/bundles/Messages_.properties*).

Afficher la solutionCacher la solution

1. Ajout de l'attribut myInput dans Test1controller :

private String myInput;

public String getMyInput() {
  return myInput;
}

public void setMyInput(String myInput) {
  this.myInput = myInput;
}

2. Modification de la méthode callback :

/* JSF callback.
 * @return A String.
 */
public String callback() {
  if (myInput.length() < 2) {
	addErrorMessage(null, "TEST1.MESSAGE.SHORT");
	return null;
  }
  return "gotoWelcome";
}

3. Ajout du message dans messages_.properties* :

Exemple pour messages_fr.properties
TEST1.MESSAGE.SHORT = Le champ "myInput" doit contenir plus de 2 caractères.

4. Modification de la page test1.jsp :

<%@include file="_include.jsp"%>
  <%@include file="_navigation.jsp"%>
  <h:form>
	<h:messages/>
	<h:inputText value="#{test1Controller.myInput}" required="true"/>
	<h:commandButton value="Move"
	action="#{test1Controller.callback}"/>
  </h:form>

Validation des formulaires

Utilisation des validateurs prédéfinis

Il existe des validateurs par défaut dans JSF (validateLength, validateLongRange et validateDoubleRange), par exemple :

<h:inputText id="age" value="#{testController.age}">
  <f:validateLongRange minimum="18"/>
</h:inputText>
<h:message for="age"/>

On peut trouver d'autres validateurs que ceux fournis par défaut, Tomahawk propose par exemple validateCreditCard, validateUrl, validateEmail, validateEqual et validateRegExpr.

Exercice : Utiliser un validateur prédéfini Afficher l'énoncéCacher l'énoncé

Ajouter un validateur prédéfini à la boite de saisie et supprimer le code correspondant de la méthode callback() (utiliser le validateur validateLength)

Afficher la solutionCacher la solution

1. Modification de la méthode callback :

/**
 * JSF callback.
 * @return A String.
 */
public String callback() {
  return "gotoWelcome";
}

2. Modification de test1.jsp :

<%@include file="_include.jsp"%>
  <%@include file="_navigation.jsp"%>
  <h:form>
    <h:messages/>
    <h:inputText value="#{test1Controller.myInput}" required="true">
	<f:validateLength minimum="3"/>
    </h:inputText>
    <h:commandButton value="Move"
	action="#{test1Controller.callback}"/>
  </h:form>

Développement de validateurs personnalisés

Il est aussi possible d'écrire ses propres validateurs. Leur mise en œuvre est relativement simple, par exemple :

<h:inputText
    id="age"
    value="#{testController.age}"
    validator="#{bean.validateAge}">
</h:inputText>

La méthode validateAge de bean ressemblera à :

public void validateAge(
    FacesContext context,
    UIComponent componentToValidate,
    Object value) throws ValidatorException {
  if (...) {
    throw new ValidatorException(getFacesErrorMessage("MESSAGE.ALWAYS_ERROR"));
  }
}
Exercice : Écrire un validateur Afficher l'énoncéCacher l'énoncé

Ajouter un validateur personnalisé validateMyInput() à la boite de saisie qui se charge de lancer une exception à la place de la méthode callback().

Afficher la solutionCacher la solution

1. Ajout dans Test1Controller:

/**
 * @param context
 * @param componentToValidate
 * @param value
 * @throws ValidatorException
 */
public void validateMyInput(FacesContext context,
  UIComponent componentToValidate,
  Object value) throws ValidatorException {
  String strValue = (String) value;
  if (strValue.length() < 2) {
	throw new ValidatorException(getFacesErrorMessage("TEST1.MESSAGE.SHORT"));
  }
}

2. Modification test1.jsp :

<%@include file="_include.jsp"%>

 <%@include file="_navigation.jsp"%>
 <h:form>
   <h:messages/>
   <h:inputText value="#{test1Controller.myInput}" required="true"
      validator="#{test1Controller.validateMyInput}"/>
   <h:commandButton value="Move"
	action="#{test1Controller.callback}"/>
 </h:form>

Mise à jour de propriétés par les formulaires (updateActionlistener)

updateActionListener est une balise de la librairie JSF Tomahawk qui est particulièrement utile. C'est un listener qui est associé à une balise permettant une action (bouton, lien) qui, au moment où ce dernier est activé, va lire le contenu de son attribut value pour l'assigner à la référence contenue dans son attribut property.On utilisera par exemple :

<h:commandButton
    action="deleteUser"
    value="#{msgs['BUTTON.DELETE']}">
  <t:updateActionListener
      value="#{user}"
      property="#{controller.userToDelete}" />
</h:commandButton>

Ici, l'action deleteUser va diriger l'utilisateur vers une autre page (typiquement une page de demande de confirmation avant d'effacer l'administrateur), via le fichier de règles de navigation. La présence de la balise updateActionListener fait que le moteur JSF aura, avant d'effectuer le changement de page, positionné la valeur de #{user} dans l'attribut userToDelete du contrôleur controller. La page de confirmation de l'effacement pourra alors faire appel à ce contrôleur pour générer un contenu en fonction de la valeur de cet attribut.

Il est à noter que, dans cet exemple JSF, user est un objet de type complexe et pas simplement une chaîne de caractères comme on peut en avoir l'habitude en développement web classique.

Exercice : Utiliser un updateActionListener Afficher l'énoncéCacher l'énoncé

Faire une vue test2.jsp qui s'appuie sur un contrôleur test2Controller qui possède une propriété chaîne de caractères value. Cette vue test2.jsp ne fait qu'afficher la valeur de value. Rajouter un bouton SetTest2Value à la vue test1.jsp et faire en sorte que l'appuie de ce bouton mettre dans test2Controller.value la valeur de myInput.

Rajouter au passage une entrée dans la barre de navigation pour aller sur la vue test2.jsp depuis n'importe quelle page de l'application.

Afficher la solutionCacher la solution

1. Création de Test2Controller qui extends AbstractContextAwareController et qui contient l'attribut value :

private String value;
public String getValue() {
  return value;
}
public void setValue(String value) {
  this.value = value;
}

2. Ajout dans controller.xml :

<bean id="test2Controller"
  class="org.esupportail.formation.web.controllers.Test2Controller"
  parent="abstractContextAwareController"
  scope="session"
  >
  <description>A bean to manage the test2 page.</description>
</bean>

3. Création de la page test2.jsp:

<%@include file="_include.jsp"%>
 <%@include file="_navigation.jsp"%>
 <h:form>
  <h:outputText value="#{test2Controller.value}"/>
 </h:form>

4. Modification du navigation-rules.xml :

<navigation-rule>
  <from-view-id>/stylesheets/test1.jsp</from-view-id>
  <navigation-case>
    <from-outcome>gotoWelcome</from-outcome>
    <to-view-id>/stylesheets/welcome.jsp</to-view-id>
    <redirect />
  </navigation-case>
  <navigation-case>
    <from-outcome>gotoTest2</from-outcome>
    <to-view-id>/stylesheets/test2.jsp</to-view-id>
    <redirect />
  </navigation-case>
</navigation-rule>

5. Modification de test1.jsp :

<%@include file="_include.jsp"%>
  <%@include file="_navigation.jsp"%>
  <h:form>
    <h:messages/>
    <h:inputText value="#{test1Controller.myInput}"
        validator="#{test1Controller.validateMyInput}"/>
    <h:commandButton value="Move"
      action="#{test1Controller.callback}"/>
    <h:commandButton value="SetTest2Value"
      action="gotoTest2">
       <t:updateActionListener value="#{test1Controller.myInput}"
	  property="#{test2Controller.value}"/>
    </h:commandButton>
  </h:form>

Ordre des opérations en JSF

  • Appel des validateurs,
  • Affectation des valeurs des entrées de formulaires,
  • Appel des updateActionListeners,
  • Appel de la callback d'action du formulaire.

Conversion des types complexes

Il existe des convertisseurs par défaut dansJSF (DateTimeConverter et NumberConverter). Ils permettent de transformer une date ou un nombre suivant différentes règles, par exemple :

<h:outputText value="#{testController.date}">
  <f:convertDateTime dateStyle="short"locale="#{sessionController.locale}"/>
</h:outputText>

Dans certains cas, il est aussi nécessaire de définir des convertisseurs manuellement. C'est notamment le cas pour les listes déroulantes. Considérons que l'on veuille afficher une liste déroulante permettant à l'utilisateur de choisir un objet de type Locale. Pour la génération du HTML, JSF a besoin d'une représentation textuelle de chaque objet de la liste. De même, il a besoin de pouvoir retrouver un objet depuis un choix de l'utilisateur qui correspond à la valeur textuelle de la balise <option> du formulaire HTML. C'est le rôle du convertisseur converter de faire ce travail :

<h:selectOneMenu id="locale" onchange="submit();"
  value="#{preferencesController.locale}"
  converter="#{localeConverter}">
  <f:selectItems value="#{preferencesController.localeItems}"/>
</h:selectOneMenu>

Ici, la liste déroulante est constituée d'objets de type Locale. Le convertisseur localeConverter est défini (par convention dans esup-commons) dans le fichier /properties/web/converters.xml :

<bean id="localeConverter"
  class="org.esupportail.commons.web.converters.LocaleConverter">
  <description>
     A converterforLocale objects.
  </description>
</bean>

La classe LocaleConverter implémente l'interface Converter de JSF.

JSF et accessibilité

Un des objectifs de l'utilisation de JSF est, via un standard de haut niveau, de tendre vers plus d'accessibilité (WAI). L'accessibilité des applications doit être une préoccupation constante des programmeurs, qui ne doivent pas hésiter à tester leurs applications en utilisant des navigateurs pauvres, tel Lynx. En règle générale, on évitera absolument d'utiliser les balises h:commandLink qui à cause de l'utilisation de Javascript brisent toutes les règles de l'accessibilité. On préfèrera dans tous les cas des boutons (e:commandButton). Javascript peut néanmoins être utilisé pour améliorer l'IHM des applications, en faisant attention à ce que les navigateurs ne parlant pas Javascript puissent quand même utiliser l'application. Nous montrons ici à titre d'exemple comment on peut soumettre un formulaire par simple changement de la valeur d'une boite déroulante :

<h:form id="form">
  <h:messages />
  <h:outputLabel for="value" value="#{msgs['TEXT.VALUE']}"/>
  <h:selectOneMenu id="value" onchange="submit();" value="#{controller.value}">
    <f:selectItems value="#{controller.valueItems}"/>
  </h:selectOneMenu>
  <h:commandButton value="#{msgs['BUTTON.CHANGE']}" id="changeButton"/>
</h:form>
<script type="text/javascript">
  hideButton("form:changeButton");
</script>

Le bouton changeButton est caché par du codeJavascript.
Les clients qui parlent Javascript ne le voient pas ; ce n'est pas grave puisqu'un changement de valeur de la boite de sélection déroulante soumet automatiquement le formulaire (onchange="submit();"). Les clients qui ne parlent pas Javascript voient le bouton, et peuvent donc valider le formulaire.

  • Aucune étiquette