W3C

XForms pour les développeurs HTML (VERSION DE TRAVAIL)

Steven Pemberton, W3C/CWI

Alain Couthures, agenceXML, traduction en français

Date de la version : 2010-08-27 (traduction : 2011-03-28)

Introduction

XForms est un langage XML pour créer des formulaires et des applications sur Internet. Ce document est une rapide introduction à XForms; même s'il suppose une connaissance des formulaires HTML et qu'il montre comment convertir des formulaires HTML en leur équivalent XForms, il peut tout de même être lu par ceux qui ne commencent pas le langage HTML. Bien qu'il soit fait mention de spécificités de XForms au delà de ce qu'il est possible de faire avec des formulaires HTML (identifiées d'une étoile au niveau des titres), il ne s'agit pas d'un tutoriel exhaustif sur toutes les fonctionnalités de XForms. La seconde partie traite des fonctionnalités qui ne sont pas disponibles dans les formulaires HTML. Une précédente version de ce tutoriel présentait XForms 1.0. Cette version a été mise à jour pour XForms 1.1.

Table des matières

*Fonctionnalités non présentes dans les formulaires HTML

Un formulaire de recherche simple

Voici un formulaire HTML simple :

<html>
<head><title>Recherche</title></head>
<body>
    <form action="http://exemple.fr/recherche"
          method="get">
         Recherche <input type="text" name="q">
         <input type="submit" value="Lancer">
    </form>
</body>
</html>

La principale différence en XForms réside dans le fait que les précisions sur les valeurs saisies et sur la façon de les envoyer sont regroupées dans l'entête (head), dans un élément appelé model; seuls les contrôles du formulaire sont placés dans le corps (body). Pour ce cas, le minimum à mettre dans l'entête est :

<model>
   <submission action="http://exemple.fr/recherche"
               method="get"/>
</model>

(Les éléments et attributs en XForms sont en minuscules.)

L'élément <form> n'est plus nécessaire; les contrôles dans le corps se présentent comme ceci :

<input ref="q"><label>Recherche</label></input>
<submit><label>Lancer</label></submit>

On y voit que les contrôles de formulaires ont un élément fils <label>, que le contrôle <input> utilise "ref" et non pas "name" et qu'il y a un contrôle submit séparé qui fait le lien avec les détails de la soumission dans l'entête. Ainsi l'exemple complet est :

<h:html xmlns:h="http://www.w3.org/1999/xhtml"
       xmlns="http://www.w3.org/2002/xforms">
<h:head>
    <h:title>Recherche</h:title>
    <model>
        <submission action="http://exemple.fr/recherche"
                    method="get"/>
    </model>
</h:head>
<h:body>
    <h:p>
        <input ref="q"><label>Recherche</label></input>
		<submit><label>Lancer</label></submit>
    </h:p>
</h:body>
</h:html>

Préfixes d'espace de nommage

Une autre différence qui saute aux yeux est l'usage des préfixes h: pour les éléments HTML. Ceci n'est pas lié à XForms mais à XML qui est conçu pour autoriser la combinaison de différents langages ensembles. XForms est conçu pour être combiné avec d'autres langages, pas uniquement XHTML. Les processeurs XML doivent savoir à quels langages les différents éléments appartiennent, même si un langage peut être choisi par défaut. Dans le document ci-dessus, XForms est utilisé par défaut, XHTML aurait tout aussi bien pu être choisi comme langage par défaut en changement les attributs xmlns dans l'entête :

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:xf="http://www.w3.org/2002/xforms">
<head>
    <title>Recherche</title>
    <xf:model>
        <xf:submission action="http://exemple.fr/recherche"
                       method="get"/>
    </xf:model>
</head>
<body>
    <p>
        <xf:input ref="q"><xf:label>Recherche</xf:label></xf:input>
        <xf:submit><xf:label>Lancer</xf:label></xf:submit>
    </p>
</body>
</html>

Une autre technique consiste à passer d'un espace de nommage par défaut à un autre, chaque fois que nécessaire, comme ceci :

<html xmlns="http://www.w3.org/1999/xhtml"
<head>
    <title>Recherche</title>
    <model xmlns="http://www.w3.org/2002/xforms">
        <submission action="http://exemple.fr/recherche"
                    method="get"/>
    </model>
</head>
<body>
    <group xmlns="http://www.w3.org/2002/xforms">
        <input ref="q"><label>Recherche</label></input>
        <submit><label>Lancer</label></submit>
    </group>
</body>
</html>

C'est au choix. On peut tout aussi bien n'en choisir aucun comme défaut et préfixer tous les éléments. On peut choisir n'importe quel préfixe; h: ou x: ou html: ou form:, la décision est libre (ou du moins, devrait l'être; certaines implémentations demandent, en effet, à ce que les éléments HTML restent sans préfixe et que les éléments XForms soient préfixés).

Contrôles de formulaires

XForms fournit des équivalents pour tous les contrôles des formulaires HTML. Mais il y a une importante différence dans l'approche : HTML principalement spécifie ce qu'un contrôle doit faire. Ainsi, alors que les spécifications HTML disent que l'élément select crée un menu et que l'élément input de type radio crée des boutons radio, XForms a les éléments select et select1, qui spécifient seulement le rôle des contrôles, sélectionner zéro ou plus, ou bien un seulement, élément dans une liste. Comment ils sont présentés à l'utilisateur peut varier selon les terminaux ou suivant une feuille de style. Sur un téléphone mobile alors que la taille de l'écran est réduite, les deux peuvent être représentés par des menus tandis que sur un écran plus grand ils pourront être représentés par des boutons à sélectionner. Une indication peut être donnée sur ce qui est souhaité ou bien une feuille de style peut être utilisée pour donner une présentation précise mais, sinon, un terminal ou une feuille de style pourra le faire à la place.

Donc voici comment s'écrivent les équivalents des contrôles dans les formulaires HTML.

Entrée simple d'un texte

Prénom : <input type="text" name="prenom">

s'écrit

<input ref="prenom"><label>Prénom :</label></input>

Il n'est pas nécessaire d'indiquer qu'il s'agit de texte : en l'absence d'autre information, par défaut c'est du texte (appelé string en XForms). Démonstration :

(Source)

Par défaut les données sont mises à jour quand on quitte le contrôle. Ajouter l'attribut incremental="true" à n'importe quel contrôle demande à ce que les données soient mises à jour à chaque frappe :

<input ref="prenom" incremental="true"><label>Prénom :</label></input>

(Source)

Voir plus loin au point 'valeurs initiales' la façon de donner une valeur initiale à n'importe quel contrôle.

Contrôles d'affichage*

Les deux exemples ci-dessus, en dehors des contrôles input, ont utilisé un contrôle qui n'est pas dans HTML : output. Ce contrôle permet d'inclure des valeurs sous forme de texte dans le document.

Le total actuel est : <output ref="somme"/>

ou

<output ref="somme"><label>Total</label></output>

Ceci peut être utilisé pour permettre à l'utilisateur de pré visualiser des valeurs tout comme pour calculer des valeurs :

Volume total : <output value="longueur * largeur * hauteur"/>

(où longueur, largeur et hauteur sont des valeurs collectées par d'autres contrôles.) Démonstration :

(Source)

L'ajout de l'attribut incremental="true" aux contrôles input au-dessus entraine que les valeurs calculées sont actualisées pendant la frappe :

(Source)

Si l'on ajoute mediatype="image/*" au contrôle, la valeur va être interprétée comme étant une URL et affichée sous forme d'image :

<output ref="photo" mediatype="image/*"/>

Zone de texte

Message : <textarea name="message" rows="20" cols="80"></textarea>

s'écrit

<textarea ref="message"><label>Message :</label></textarea>

(Source)

La présentation, typiquement la hauteur et la largeur, est à réaliser avec une feuille de style. For Par exemple, on peut rajouter ceci à la feuille de style

textarea[ref="message"] { font-family: monospace;
                          height: 20em; width: 80em }

Même si cela copie le comportement de HTML, il n'y a pas d'obligation à ce qu'une police de taille fixe soit utilisée, ni que les dimensions soient exprimées en nombre de lignes et de colonnes : on peut tout aussi bien utiliser des longueurs, des pourcentages ou quoi que ce soit d'autre :

textarea[ref="message"] { font-family: serif;
                          height: 2cm; width: 20% }

Si l'on veut que tous les contrôles textarea aient les mêmes dimensions, on peut utiliser ceci

textarea { font-family: sans-serif;
           height: 20em; width: 80em }

L'inclusion d'une feuille de style dans un document se fait comme d'habitude (par exemple en utilisant <link> ou <style> en HTML).

Boutons radio

Les boutons radio sélectionnent une valeur parmi d'autres :

Sexe :
<input type="radio" name="sexe" value="H"> Homme
<input type="radio" name="sexe" value="F"> Femme

ce qui devient

<select1 ref="sexe">
   <label>Sexe :</label>
   <item>
      <label>Homme</label><value>H</value>
   </item>
   <item>
      <label>Femme</label><value>F</value>
   </item>
</select1>

Il faut garder à l'esprit que ceci peut, tout aussi bien, être présenté comme des boutons radio, une liste déroulante ou un menu. On peut ajouter l'indication appearance="full" sur l'élément <select1> pour suggérer qu'il devrait être restitué sous forme de boutons radio. (On utilisera appearance="compact" pour suggérer une liste déroulante et appearance="minimal" pour suggérer un menu). Voici un exemple - on remarquera que, parce que les contrôles sont tous associés à la même valeur de donnée, si l'on en change un, les autres se synchronisent en conséquence :

(Source)

La section sur 'les valeurs initiales' ci-dessous dit comment présélectionner une valeur.

Cases à cocher

Les cases à cocher permettent de sélectionner 0 ou plus dans une liste.

Parfums :
<input type="checkbox" name="parfums" value="v"> Vanille
<input type="checkbox" name="parfums" value="f"> Fraise
<input type="checkbox" name="parfums" value="c"> Chocolat

s'écrit

<select ref="parfums" appearance="full">
   <label>Parfums :</label>
   <item>
      <label>Vanille</label><value>v</value>
   </item>
   <item>
      <label>Fraise</label><value>f</value>
   </item>
   <item>
      <label>Chocolat</label><value>c</value>
   </item>
</select>

(Source)

La section sur 'les valeurs initiales' ci-dessous dit comment présélectionner une valeur.

Selon la présence de l'attribut multiple en HTML, les menus sélectionnent un ou zéro ou plus d'options dans une liste. On utilise soit <select1> pour ne sélectionner qu'un seul choix ou <select> pour en sélectionner zéro ou plus.

Mois :
<select multiple name="printemps">
      <option value="Mar">Mars</option>
      <option value="Avr">Avril</option>
      <option>Mai</option>
</select>

s'écrirait :

<select ref="printemps" appearance="minimal">
<label>Mois :</label>
<item><label>Mars</label><value>Mar</value></item>
<item><label>Avril</label><value>Avr</value></item>
<item><label>Mai</label><value>Mai</value></item>
</select>

Si l'attribut multiple n'est pas présent sur l'élément HTML select, alors on utilise select1.

La section sur 'les valeurs initiales' ci-dessous dit comment présélectionner une valeur.

Sélection de fichier

<form method="post" enctype="multipart/form-data" ...>
 ...
Fichier : <input type="file" name="piecejointe">

s'écrit

<submission method="form-data-post" .../>
 ...
<upload ref="piecejointe"><label>Fichier :</label></upload> 

Mots de passe

Mot de passe : <input type="password" name="motdepasse">

s'écrit

<secret ref="motdepasse"><label>Mot de passe :</label></secret>

Cet exemple utilise un contrôle secret et un contrôle supplémentaire pour optionnellement afficher ce qui a été saisi :

(Source)

Réinitialisation

Dix années d'expérience en formulaires HTML ont montré que bien peu utilisent effectivement les boutons de réinitialisations alors que de très nombreux formulaires Web les incluent. Ce qui pose problème c'est que, souvent, le bouton de réinitialisation avec le texte "Reset" est plus grand que le bouton de soumission qui est souvent marqué de "OK", avec pour conséquence que des gens cliquent par erreur sur le bouton de réinitialisation alors qu'ils voulaient cliquer sur OK, en perdant au passage beaucoup de leur travail (moins si les navigateurs disposent d'une commande d'annulation). Aussi, même s'il est possible de créer un bouton de réinitialisation en XForms, c'est volontairement plus difficile à faire, afin de décourager tous ceux qui n'en voudraient pas absolument :

<input type="reset">

s'écrit ainsi

<trigger>
   <label>Réinitialiser tous les champs</label>
   <reset ev:event="DOMActivate"/>
</trigger>

(Source)

Une différence importante à noter avec la version HTML est que les champs sont réinitialisés à leur valeur initiale respective et qu'ils ne sont pas juste vidés (ceci peut être observé dans l'exemple ci-dessus si l'on change les valeurs puis que l'on appuie sur le bouton de réinitialisation).

Le fait que l'attribut event ait un préfixe implique qu'il faut ajouter la déclaration d'espace de noms XML suivante en tête :

xmlns:ev="http://www.w3.org/2001/xml-events"

L'explication complète du marquage des événements se trouve en partie 2.

Boutons

Les boutons n'ont pas de comportement prédéfini, le comportement qui leur est attaché est déclenché quand l'événement correspondant se produit.

L'élément button

<input type="button" value="Afficher" onclick="afficher()">

s'écrit

<trigger><label>Afficher</label>
   <h:script ev:event="DOMActivate" type="text/javascript">afficher()</h:script>
</trigger>

ou

<trigger ev:event="DOMActivate" ev:handler="#afficher">
    <label>Afficher</label>
</trigger>

lorsque "#afficher" permet de retrouver l'élément (par exemple un élément script) qui implémente le comportement :

<script id="afficher" ...>...

XForms incorpore de nombreuses actions qui peuvent être exécutées par un bouton; voir le bouton reset dans l'exemple ci-dessus.

Boutons image

<input type="image" src="..." ...>

peut s'écrire de différentes manières, comme en mettant une image dans l'élément label :

<trigger...><label><h:img src="..." .../></label></trigger>

ou en l'indiquant dans une feuille de style

<trigger id="activate" ...>

avec une règle de feuille de style

trigger#activate {background-image: url(button.png);
                  background-repeat: none}

ou en utilisant l'élément <output> de XForms :

<trigger><label><output mediatype="image/*" value="'http://www.exemple.fr/image.png'"/></label></trigger>

(De la même manière pour <submit>.)

Groupes d'options

Boisson :
<select name="boisson">
   <option selected value="rien">Aucune</option>
   <optgroup label="Non alcoolisées">
      <option value="h2o">Eau</option>
      <option value="l">Lait</option>
      <option value="j">Jus de fruit</option>
   </optgroup>
   <optgroup label="Vins et spiritueux">
      <option value="vr">Vin rouge</option>
      <option value="vb">Vin blanc</option>
      <option value="b">Bière</option>
   </optgroup>
</select>

s'écrit

<select1 ref="boisson">
   <label>Boisson :</label>
   <item><label>Aucune</label><value>rien</value></item>
   <choices>
      <label>Non alcoolisées</label>
      <item><label>Eau</label><value>h2o</value></item>
      <item><label>Lait</label><value>l</value></item>
      <item><label>Jus de fruit</label><value>j</value></item>
   </choices>
   <choices>
      <label>Vins et spiritueux</label>
      <item><label>Vin rouge</label><value>vr</value></item>
      <item><label>Vin blanc</label><value>vb</value></item>
      <item><label>Bière</label><value>b</value></item>
   </choices>
</select1>

Groupement de contrôles

<fieldset>
   <legend>Informations personnelles</legend>
   Nom : <input name="nom" type="text">
   Prénom : <input name="prenom" type="text">
   Adresse : <input name="adresse" type="text">
</fieldset>

s'écrit

<group>
   <label>Informations personnelles</label>
   <input ref="nom"><label>Nom :</label></input>
   <input ref="prenom"><label>Prénom :</label></input>
   <input ref="adresse"><label>Adresse :</label></input>
</group>

On remarquera l'utilisation pertinente de l'élément <label>.

Contrôles de plage*

Il y a un autre contrôle, indépendamment de output qui n'est pas dans HTML, il s'agit de range. Ce contrôle permet de spécifier une contrainte sur une valeur.

<range ref="volume" start="1" end="11" step="0.5"/>

Ceci peut visuellement être représenté par un curseur ou un équivalent.

Contrôles cachés

Comme l'on pourra le voir dans la section suivant, il n'y a pas besoin d'avoir des contrôles cachés en XForms.

Valeurs envoyées

L'attribut ref sur chaque contrôle en fait référence un élément descendant d'un élément instance dans le modèle, pour réunir les valeurs avant la soumission. S'il n'y a pas d'élément instance (comme dans l'exemple de recherche ci-dessus), il en est créé un implicitement.

Rendre explicites les valeurs envoyées

Bien qu'il soit tout à fait autorisé de laisser le système créer l'instance lui-même, il y a de bonnes raisons de toujours en inclure une, comme dans l'exemple de recherche :

<model>
    <instance><donnees xmlns=""><q/></donnees></instance>
    <submission action="http://exemple.fr/recherche"
                method="get"/>
</model>

On voit immédiatement que la seule valeur soumise se nomme "q". L'avantage le plus évident de ceci est de permettre de voir ce qui est soumis mais le système va également vérifier que, lorsque l'on indique ref="q", il y a bien un élément q dans l'instance.

Il faut noter qu'il est essentiel, pour des raisons non détaillées ici, de mettre la déclaration xmlns="" dans l'instance de données afin de dire au processeur que les éléments en présence ne sont ni des éléments XHTML ni des éléments XForms.

(L'étiquette <data> utilisée ici pourrait être renommée selon convenance)

Valeurs initiales

L'initialisation des contrôles (les cases à cocher, les sélections dans les menus, etc.) se réalise simplement en donnant une instance avec des valeurs pré remplies. Dans le cas de l'exemple de recherche :

<instance><donnees xmlns=""><q>Mots-clés</q></donnees></instance>

pré remplirait le contrôle avec le mot Mots-clés.

De la même manière pour l'exemple dans la section cases à cocher qui se présente comme suit :

<select ref="parfums" appearance="full">
   <label>Parfums :</label>
   <item>
      <label>Vanille</label><value>v</value>
   </item>
   <item>
      <label>Fraise</label><value>f</value>
   </item>
   <item>
      <label>Chocolat</label><value>c</value>
   </item>
</select>

Il est possible de pré selectionner les cases à cocher vanille et fraise comme ceci :

<instance><donnees xmlns=""><parfums>v f</parfums></donnees></instance>

Idem pour l'exemple de menus qui se présente comme suit :

<select ref="printemps" appearance="minimal">
<label>Mois :</label>
<item><label>Mars</label><value>Mar</value></item>
<item><label>Avril</label><value>Avr</value></item>
<item><label>Mai</label><value>Mai</value></item>
</select>

La pré selection de Mars et Avril s'effectue comme ceci :

<instance><donnees xmlns=""><printemps>Mar Avr</printemps></donnees></instance>

Et, pour l'exemple optgroup :

<select1 ref="boisson">
   <label>Boisson :</label>
   <item><label>Aucune</label><value>rien</value></item>
   <choices>
      <label>Non alcoolisées</label>
      <item><label>Eau</label><value>h2o</value></item>
      <item><label>Lait</label><value>l</value></item>
      <item><label>Jus de fruit</label><value>j</value></item>
   </choices>
   <choices>
      <label>Vins et spiritueux</label>
      <item><label>Vin rouge</label><value>vr</value></item>
      <item><label>Vin blanc</label><value>vb</value></item>
      <item><label>Bière</label><value>b</value></item>
   </choices>
</select1>

On pré selectionne la valeur rien comme ceci :

<instance><donnees xmlns=""><boisson>rien</boisson></donnees></instance>

Valeurs cachées

La raison pour laquelle XForms n'a pas besoin de contrôles cachés est que toutes les valeurs dans l'instance qui ne sont pas liées à un contrôle sont, par définition, non visibles pour l'utilisateur. Aussi, si l'on veut ajouter une valeur cachée resultats au formulaire de recherche, on change l'instance ainsi :

<instance><donnees xmlns=""><q/><resultats>10</resultats></donnees></instance>

Récupérer les valeurs initiales d'ailleurs*

Il n'est pas nécessaire de spécifier l'instance initiale dans le document lui-même car il peut être chargé depuis une ressource externe comme ceci :

<instance src="http://exemple.org/forms/templates/t21.xml"/>

et le document t21.xml peut contenir des données telles que

<donnees><l>640</l><h>480</h><p>8</p></donnees>

(xmlns="" n'est pas nécessaire dans les ressources externes même si cela ne gène en rien.)

"Editer" n'importe quel document XML*

Le fait de pouvoir charger des ressources externes donne un immense pouvoir. Ceci est dû à ce que l'attribut ref associé aux contrôles de formulaires n'est pas limité à la sélection d'un identifiant tel que name de HTML mais en fait à n'importe quelle expression XPath. XPath permet de sélectionner n'importe quel élément ou attribut dans un document XML.

Cela signifie qu'après avoir fait l'apprentissage de XPath, on peut considérer tout document XML comme une instance, même un document XHTML, on peut y lier des contrôles et on peut en faire l'envoi. Par exemple, pour faire un lien avec l'élément <title> d'un document XHTML, on peut utiliser

<input ref="/h:html/h:head/h:title">...

(c'est-à-dire l'élément title à l'intérieur de l'élément head à l'intérieur de l'élément html, tous étant dans l'espace de nommage XHTML) ou

<input ref="/h:html/h:body/@class">...

qui est l'attribut class de l'élément body.

Par exemple, imaginons un salon de thé avec des horaires d'ouverture variables (peut-être selon la météo) pour laquelle on veut avoir une page Web où les internautes pourront voir s'il est ouvert. Supposons que la page en question ait un unique paragraphe en corps de document :

<p>Le salon est <strong>fermé</strong> aujourd'hui.</p>

Eh bien, plutôt que d'apprendre au personnel du salon à écrire de l'HTML pour la mise à jour, il est facile de faire un formulaire pour, plutôt, éditer la page :

<model>
   <instance      src="http://www.exemple.fr/shop/status.xhtml"/>
   <submission action="http://www.exemple.fr/shop/status.xhtml"
               method="put"/>
</model
 ...
<select1 ref="/h:html/h:body/h:p/h:strong"><label>Le salon est maintenant :</label>
    <item><label>ouvert</label><value>ouvert</value></item>
    <item><label>fermé</label><value>fermé</value></item>
</select1>
<submit><label>Enregistrement</label></submit>

(Cet exemple charge bien le fichier XHTML ci-dessus mais l'enregistrement ne sera pas effectif :-) )

(Source)

Il faut bien noter que, pour que cela fonctionne, la page en question doit être en XHTML bien formé, (et non pas en HTML, car seule une page XHTML est un document XML), et que le serveur doit accepter la méthode "put" (pas tous les serveurs le font). Autrement, si le document XHTML en question est dans un fichier sur la machine locale, il est possible de récupérer l'instance depuis le fichier puis de la réécrire avec un envoi. Voir plus loin.

Envoi

Envois multiples*

HTML n'autorise l'envoi de données qu'à un serveur et dans un seul sens.

XForms autorise la déclaration de différents contrôles d'envoi eux-mêmes liés à différents éléments submission dans l'élément head pour envoyer les données à différents serveurs ou de différentes manières.

Par exemple, l'exemple de recherche pourrait permettre à l'utilisateur d'envoyer la chaine de recherche à différents moteurs de recherche :

<model>
   <instance><donnes xmlns=""><q/></donnees></instance>
   <submission action="http://exemple.fr/recherche"
               method="get" id="fr"/>
   <submission action="http://exemple.org/recherche"
               method="get" id="org"/>
</model>

et dans l'élément body :

<submit submission="org"><label>Rechercher sur exemple.org</label></submit>
<submit submission="fr"><label>Rechercher sur exemple.fr</label></submit>

On remarquera que le choix de l'élément submission à utiliser est identifié par l'attribut 'id'. Le premier élément submission dans le modèle est pris par défaut, ainsi il est aussi possible d'écrire :

<model>
   <instance><donnees xmlns=""><q/></donnees></instance>
   <submission action="http://exemple.fr/recherche"
               method="get"/>
   <submission action="http://exemple.org/recherche"
               method="get" id="org"/>
</model>

et dans l'élément body :

<submit submission="org"><label>Rechercher sur exemple.org</label></submit>
<submit><label>Rechercher sur exemple.fr</label></submit>

mais il faut noter que le risque en faisant cela est qu'il ne faut pas changer l'ordre des éléments submission.

Méthodes d'envoi

Comme avec HTML il y a plusieurs façons d'envoyer des données. En HTML la manière d'envoyer s'exprime au travers de deux attributs, method et enctype; en XForms cela s'exprime uniquement avec method :

Equivalence des méthodes d'envoi entre HTML et XForms
HTML XForms
method="get" method="get"
method="post"
enctype="application/x-www-form-urlencoded"
method="urlencoded-post"
method="post"
enctype="multipart/form-data"
method="form-data-post"

Il y a de nouvelles façons de procéder à un envoi; les plus intéressantes sont method="post" qui poste les résultats en tant que document XML et method="put" qui récupère les résultats en tant que document XML. Une utilisation intéressante de ceci est la suivante :

<submission action="file://resultats.xml" method="put"/>

qui sauvegarde les résultats parmi les fichiers locaux en utilisant la notation file://.

Comme, voir ci-dessus, on peut avoir plus d'un envoi par formulaire, il est possible pour un formulaire de grande taille d'avoir distinctement des boutons 'Sauvegarder sur disque' et 'Envoyer'.

L'après envoi*

Le comportement par défaut quand des valeurs sont envoyées est que le résultat retourné par le serveur remplace le document tout entier, exactement comme en HTML. Cependant, il y a d'autres options, spécifiées par l'attribut replace sur l'élément submission. La valeur replace="instance" ne remplace que l'instance et la valeur replace="none" laisse le formulaire en l'état sans le remplacer.

Par exemple, pour faire un formulaire de changement d'adresse auprès d'une banque, on peut prévoir deux boutons, un pour pré remplir le formulaire avec le nom et l'adresse selon le numéro de compte et un autre pour envoyer les données modifiées :

<model>
    <instance><donnees xmlns="">
        <numcompte/><nom/><adresse/>
    </donness></instance>
    <submission method="get" action="http://exemple.fr/prefill"
                id="prefill" replace="instance"/>
    <submission method="get" action="http://exemple.fr/change"
                id="change" replace="none"/>
</model>
 ...
<input ref="numcompte"><label>Numéro de compte</label></input>
<submit submission="prefill"><label>Récupérer</label></submit>
<input ref="nom"><label>Nom</label></input>
<textarea ref="adresse"><label>Adresse</label></textarea>
<submit submission="change"><label>Envoyer</label></submit>

Le bouton 'Récupérer' va remplacer l'instance par une nouvelle instance contenant les détails de la personne associée au numéro de compte que l'on peut alors modifier; le bouton 'Envoyer' va alors renvoyer l'instance modifiée, laissant le formulaire en l'état dans le navigateur pour permettre d'autres changements ou pour rentrer un autre numéro de compte.

Ceci ressemblera à cela (il ne s'agit que d'une simulation où les valeurs modifiées ne sont pas envoyées, juste là pour donner l'idée du principe) :

(Source)

Contrôler les contrôles

En HTML, il est possible de spécifier que des contrôles sont désactivés ou en lecture seule mais le seul moyen pour changer cette propriété est d'utiliser un langage de script.

XForms met à disposition des moyens simples pour contrôler non seulement ces propriétés mais d'autres encore :

On notera que, en XForms, c'est la valeur collectée qui dispose de la propriété, et non pas le contrôle, mais que la propriété s'applique à tous les contrôles liés à la valeur.

Ces propriétés utilisent un élément <bind> qui se dépose dans l'élément <model>. Pour pouvoir utiliser un élément bind, il faut avoir un élément <instance> explicite.

Contrôles inhibés

Pour inhiber des contrôles, il faut utiliser la propriété relevant. Par exemple, pour dire que le numéro de carte de crédit n'est à remplir que si la personne paie par carte, on écrira :

<model>
   <instance><donnees xmlns="">
      <montant/><moyen/><numcarte/><expiration/>
   </donnees></instance>
   <bind nodeset="numcarte" relevant="../moyen='carte'"/>
   <bind nodeset="expiration" relevant="../moyen='carte'"/>
</model>

Ceci commande que les champs numcarte et expiration ne sont effectifs que lorsque moyen a la valeur carte et qu'ils seront dès lors inhibés pour les autres valeurs de moyen. Il faut écrire "../moyen", plutôt que seulement moyen, parce que, dans un élément bind, on se situe au niveau de la chose référencée dans l'attribut nodeset (qui peut elle-même être un élément structuré). C'est un peu comme si un 'changement de répertoire' avait été fait sur cet élément. Si l'on écrivait juste "moyen", cela ferait référence à un élément fils de numcarte ou expiration. On peut également utiliser un adressage absolu, comme /donnees/moyen qui produirait le même effet que ../moyen pour ce cas.

Les contrôles peut être écrits ainsi (il faut bien remarquer qu'il y a aucune indication selon laquelle ils pourraient être inhibés: ceci est hérité de la valeur qu'ils référencent):

<select1 ref="moyen"><label>Moyen de paiement:</label>
   <item><label>Espèces</label><value>especes</value></item>
   <item><label>Carte de crédit</label><value>carte</value></item>
</select1>
<input ref="numcarte"><label>Numéro de carte :</label></input>
<input ref="expiration"><label>Date d'expiration:</label></input>

Démonstration :

(Source)

Ceci peut être simplifié en utilisant une instance structurée :

<model>
   <instance><donnees xmlns="">
      <montant/><moyen/>
      <carte>
        <numero/><expiration/>
      </carte>
   </donnees></instance>
   <bind nodeset="carte" relevant="../moyen='carte'"/>
</model>

et les contrôles référencent alors les enfants de 'carte':

<input ref="carte/numero"><label>Numéro de carte :</label></input>
<input ref="carte/expiration"><label>Date d'expiration:</label></input>

à moins d'utiliser un regroupement pour positionner le contexte des attributs ref:

<group ref="carte">
   <input ref="numero"><label>Numéro de carte :</label></input>
   <input ref="expiration"><label>Date d'expiration :</label></input>
</group>

Chaque implémentation est libre de présenter comme elle veut les contrôles inhibés (et elle peut aussi permettre au développeur de spécifier dans une feuille de style comme les restituer) mais, typiquement, ils sont soit grisés soit purement et simplement cachés. La règle de style pourrait ressembler à ceci :

input::disabled {display: none}

Contrôles en lecture seule

De la même manière que pour relevant, on peut spécifier une condition selon laquelle une valeur est en lecture seule. Par exemple :

<model>
   <instance><donnees xmlns="">
      <option>basique</option><couleur>noir</couleur>
   </donnees></instance>
   <bind nodeset="couleur" readonly="../option='basique'"/>
</model>

Cet exemple dit que la valeur par défaut de couleur est noir qu'elle ne peut pas être changée si option a la valeur basique. Démonstration :

(Source)

(Cet exemple va être modifié ci-après pour que la valeur de la couleur soit aussi réinitialisée à noir si l'option est repositionnée à basique. Voir la propriété calculate un peu plus loin).

La règle de style pour un élément en lecture seule est

input::read-only {...}

Contrôles obligatoires*

Une nouveauté intéressante avec XForms est de pouvoir imposer qu'une valeur soit saisie avant que l'envoi soit autorisé. Le moyen le plus simple pour cela est de dire juste que cette valeur est toujours obligatoire. Par exemple, dans l'exemple de recherche :

<model>
   <instance><donnees xmlns=""><q/></donnees></instance>
   <bind nodeset="q" required="true()"/>
   <submission .../>
</model>

mais comme pour les attributs readonly et relevant, il est possible d'utiliser n'importe quelle expression XPath pour rendre une valeur obligatoire sur condition :

<bind nodeset="etat" required="../pays='USA'"/>

ce qui veut dire que la valeur pour etat est obligatoire quand la valeur pour pays est "USA". Démonstration :

(Source)

Il appartient à chaque implémentation de décider comment indiquer qu'une valeur est obligatoire mais elle peut aussi autoriser à le définir dans une feuille de style comme ceci :

input::required { border: thin red solid }

Propriété constraint*

Cette propriété permet d'ajouter des contraintes supplémentaires à une valeur. Par exemple :

<bind nodeset="annee" constraint=". &gt; 1970"/>

ajoute la contrainte que l'année doit être après 1970. On notera que l'utilisation de "." en XPath signifie "cette valeur". (Pour rappel, ">" doit être écrit &gt; pour respecter le format XML).

Il est possible de combiner les valeurs, comme dans :

<bind nodeset="deces" constraint=". &gt;= ../naissance"/>

Démonstration :

(Source)

Un contrôle, indépendamment de son étiquette, peut aussi contenir une alerte qui explicitera quelles restrictions s'appliquent à une valeur en saisie :

<input ref="deces"><label>Année du décès</label>
    <alert>L'année du décès ne doit pas être antérieure à l'année de naissance</alert>
</input>

Propriété calculate*

Il est possible d'indiquer qu'une valeur dans une instance est calculée à partir d'autres valeurs. Par exemple :

<bind ref="dureedevie" calculate="../deces - ../naissance"/>

Quand une valeur est ainsi calculée, elle passe automatiquement en lecture seule.

(Source)

Il y a beaucoup de fonctions disponibles, parmi lesquelles des fonctions mathématiques, des fonctions de manipulation des chaines de caractères, des fonctions de gestion des dates et la fonction conditionnelle 'if'.

L'exemple sur la lecture seule ci-dessus peut être modifié pour utiliser une contrainte veillant à assurer que la valeur par défaut value de couleur s'applique toujours aux modèles basiques :

<model>
   <instance><donnees xmlns="">
      <option>deluxe</option><couleur>jaune</couleur>
   </donnees></instance>
   <bind nodeset="couleur"
         readonly="../option='basique'"
         calculate="if(../option='basique', 'noir', .)"/>
</model>

Ceci signifie que si l'option est basique, la couleur est forcée à noir sinon la couleur garde la valeur qu'elle avait déjà.

(Source)

Types*

Une autre fonctionnalité intéressante est qu'il est possible d'associer un type à une valeur. L'implémentation peut alors vérifier que les valeurs correspondent bien au type demandé.

Par exemple, si l'exemple de recherche est en fait pour ne chercher que des nombres (par exemple pour chercher dans une base de données d'anomalies), il suffit d'y rajouter :

<bind nodeset="q" type="integer"/>

Ceci va empêcher la valeur d'être envoyée à moins qu'il s'agisse d'un entier. Démonstration (cet exemple utilise également un élément alert):

(Source)

Lorsque l'on veut récupérer l'URL de la page d'accueil de quelqu'un, on peut spécifier

<bind nodeset="pageaccueil" type="anyURI"/>

On pourra rencontrer des implémentations qui ont des traitements particuliers quand elles connaissent le type de donnée d'une valeur. Par exemple, quand elles savent que la valeur est une date, elles font apparaître un sélecteur de date plutôt que de demander de taper les caractères de la date. Comme ceci :

<bind nodeset="quand" type="date"/>
 ...
<input ref="quand"><label>Date :</label><input>

(Source)

De la même manière, si une valeur est un booléen, elle sera représentée par une case à cocher :

<bind nodeset="accord" type="boolean"/>
 ...
<input ref="accord"><label>J'accepte toutes les conditions</label></input>

(Source)

Il y a beaucoup de types pré-définis, parmi lesquels :

Tous ces types sont directement intégrés à XForms. Pour utiliser des types définis ailleurs, il est possible d'utiliser un préfixe de la manière habituelle :

type="xsd:float"

(sans oublier d'ajouter la déclaration correspond au préfixe xsd).

Combiner les propriétés

Quand on a plusieurs associations se référant à la même valeur, on peut les combiner :

<bind nodeset="q" type="integer" required="true()"/>

Par exemple, un numéro de carte de crédit répond à un type de données mais les numéros de carte de crédit intègrent également un code de contrôle qui peut être représenté par une contrainte :

<bind nodeset="cc" type="card-number" constraint="is-card-number(.)"/>

Plus d'un formulaire par document

Pour disposer de plus d'un formulaire dans un document, il faut déclarer un modèle par formulaire ce qui entraine qu'il faut identifier à quel formulaire chaque contrôle correspond. Ceci se fait avec un attribut id sur chaque modèle et un attribut model sur chaque contrôle :

<model id="recherche">
   <instance><donnees xmlns=""><q/></donnees></instance>
   <submission id="r" .../>
</model>
<model id="identification">
   <instance><donnees xmlns=""><utilisateur/><motdepasse/></donnees></instance>
   <submission id="i" .../>
</model>
 ...
<input model="recherche" ref="q"><label>Recherche</label></input>
<submit submission="r"><label>Lancer</label></submit>
 ...
<input model="identification" ref="utilisateur"><label>Nom d'utilisateur</label></input>
<secret model="identification" ref="motdepasse"><label>Mot de passe</label></secret>
<submit submission="i"><label>Se connecter</label></submit>

Plus d'une instance par modèle

Il y a des situations où l'on a besoin de plus d'une instance. Par exemple, cela peut être utile pour des calculs d'avoir une instance qui contient les valeurs pour les taux de charges salariales et une autre instance pour contenir les valeurs pour une personne en particulier. Cela ne pose pas de problème car l'on peut avoir autant d'instances que l'on veut dans un modèle :

<model>
    <instance><donnees xmlns=""><nom/><age/><salaire/><tauxdecharges/></donnees></instance>
    <instance id="charges" src="charges.xml"/>
    <instance id="devises" src="devises.xml"/>
    ...
</model>

Pour identifier l'instance voulue, on utilise la fonction instance :

<input ref="instance('charges')/plancher">...
<output ref="instance('devises')/eur">...

La première instance d'un modèle est prise par défaut et les références non explicites y pointent toujours :

<input ref="age">...

La fonction instance est également utilisable dans des calculs :

<bind nodeset="tauxdecharges" calculate="if(../salaire &gt; instance('charges')/limite, instance('charges')/plafond, instance('charges')/plancher)"

bind à la place de ref

S'il y a une association bind dans le modèle, on peut y faire référence sur le contrôle plutôt que directement à la valeur. Ceci permettra de changer la structure de l'instance sans avoir à intervenir sur les contrôles. Ceci évite aussi d'avoir à spécifier quel modèle est impliqué :

<model>
   <instance><donnees xmlns=""><q/></donnees></instance>
   <submission id="r" .../>
   <bind id="requete" nodeset="q" required="true()"/>
</model>
 ...
<input bind="requete"><label>Recherche</label></input>

(Remarquer que l'attribut bind fait référence à une id sur un élément bind; il ne s'agit pas d'une expression XPath.)

Fonctionnalités non mises en avant ici

Comme indiqué dans l'introduction, ceci ne constitue pas un tutoriel complet sur XForms. Les principales fonctionnalités non traitées ici sont :

Celles-ci sont toutes traitées en détail dans la partie 2.