A propos de ce document

Le Guide de l'utilisateur Velocity a pour but d'aider les concepteurs de page et les fournisseurs de contenu à se familiariser avec Velocity et avec la syntaxe de son langage de script, simple mais puissant, le Velocity Template Language (VTL). Beaucoup d'exemples dans ce guide concernent l'utilisation de Velocity pour l'inclusion de contenu dynamique dans des sites web, mais tous les exemples du VTL pourraient aussi bien s'appliquer à d'autres pages ou gabarits.

Merci de choisir Velocity !

Velocity, qu'est-ce que c'est?

Velocity est un moteur de substitution, basé sur Java. Il permet aux concepteurs de pages web de faire référence à des méthodes définies dans du code Java. Les concepteurs de pages peuvent travailler en équipe avec des programmeurs Java pour développer des sites web dans l'architecture MVC (Modèle-Vue-Contrôleur), ce qui signifie que les infographistes peuvent se concentrer sur la création d'un site au désign attractif et les programmeurs peuvent se consacrer entièrement à l'écriture de code de qualité. Velocity sépare le code Java des pages web, ce qui rend le site plus facile à maintenir dans le long terme et fournit une alternative réaliste aux Java Server Pages (JSPs) ou à PHP.

Velocity peut être utilisé pour générer des pages web, du SQL, du Postcript et tout ce qui peut être généré à partir d'un gabarit. Vous pouvez l'utiliser comme un utilitaire indépendant pour générer du code source ou des états imprimés, ou bien comme un composant intégré dans d'autres systèmes. A terme, Velocity fournira les services d'inclusion pour le framework d'applications web Turbine. Velocity et Turbine fournissent un service d'inclusion qui permettra de développer des applications web dans une véritable architecture MVC.

Qu'est-ce que Velocity peut faire pour moi?

L'exemple de MudStore

Supposons que vous soyez un concepteur de pages pour une boutique en ligne spécialisée dans la vente de terre cuite. Appelons-la "The Online Mud Store". Les affaires marchent fort. Les clients passent commande pour différents types et diverses quantités de terre cuite. Ils s'identifient sur votre site avec un nom d'utilisateur et un mot de passe, ce qui leur permet de suivre leurs commandes et d'en passer de nouvelles. Pour l'instant, vous vendez de la glaise Terracotta, un produit qui marche bien. Quelques-uns de vos clients achètent régulièrement de la glaise Bright Red, que vous vendez aussi, bien sûr, mais qui n'a pas autant la cote et qui se trouve d'habitude reléguée dans les marges de vos pages. Les informations relatives à chaque client sont suivies dans votre base de données, et donc un jour la question se pose: pourquoi ne pas utiliser Velocity pour proposer des offres spéciales à une clientèle ciblée, celle qui est la plus intéressée à un type de marchandise?

Avec Velocity, il est très facile de personnaliser les pages web pour certains visiteurs. En tant que concepteur de pages du MudRoom, vous voulez maintenant réaliser la page d'accueil que verra votre client après s'être identifié sur votre site.

Vous tenez une réunion avec les ingénieurs développement de votre entreprise, et tout le monde s'accorde sur le fait que $customer contiendra les informations relatives au client qui s'est connecté et $mudsOnSpecial tous les types de terre disponibles à la vente en ce moment. L'objet $flogger contient différentes méthodes pour aider à la promotion de certains produits. Pour la tâche qui nous concerne, occupons-nous seulement de ces trois références. Rappelez-vous que vous n'avez pas à vous soucier de la manière dont les développeurs vont extraire les informations nécessaires de la base de données, vous supposez seulement que ça fonctionne -- ce qui vous permet de vous occuper de votre part du boulot et les développeurs de la leur.

Vous pouvez inclure l'instruction VTL suivante dans votre page web:

<HTML>
<BODY>
Hello $customer.Name!
<table>
#foreach( $mud in $mudsOnSpecial )
   #if ( $customer.hasPurchased($mud) )
      <tr>
        <td>
          $flogger.getPromo( $mud )
        </td>
      </tr>
   #end
#end
</table>

    

Les détails précis de l'instruction foreach seront décrits un peu plus loin; ce qui compte pour l'instant, c'est l'impact que peut avoir ce petit script sur votre site web. Quand un client qui apprécie habituellement la glaise BrightRed se connecte, et que ce produit est en vente, c'est ce qu'il verra en premier lieu. Si un client qui a acheté beaucoup de Terracotta se connecte, c'est la vente de Terracotta qui sera affichée en tête et au centre. La flexibilité de Velocity est très grande, limitée seulement par votre créativité.

Dans le manuel de référence du VTL, vous trouverez la documentation de beaucoup d'autres éléments de Velocity, qui ensemble vous donnent la puissance et la souplesse dont vous avez besoin pour faire de votre site web une présence sur le web. Lorsque vous deviendrez de plus en plus familiers avec ces éléments, vous mettrez à votre service toute la puissance de Velocity.

Introduction au Velocity Template Language (VTL)

Le Velocity Template Language (VTL) a été conçu pour inclure du contenu dynamique dans une page web de la manière la plus facile, la plus simple et la plus propre. Même un infographiste avec peu ou pas de bagage en programmation sera rapidement capable d'utiliser le VTL pour incorporer du contenu dynamique dans un site web.

VTL utilise des références pour embarquer du contenu dynamique dans un site web, et une variable est un type de référence. Une variable fait référence à quelque chose qui est défini dans le code Java, ou elle peut prendre sa valeur d'une instruction VTL dans la page web. Voici un exemple d'instruction VTL

#set( $a = "Velocity" )

Cette instruction VTL -- comme toutes les instructions VTL d'ailleurs -- commence par le caractère # et contient une directive set. Quand un visiteur demande votre page web, le moteur de substitution Velocity (Velocity Templating Engine) recherche dans votre page tous les caractères #, détermine lesquels marquent le début d'instructions VTL, et quels caractères # n'ont rien à voir avec le VTL.

Le caractère #est suivi d'une directive, set. La directive setutilise une expression (entre parenthèses) -- une équation qui assigne une valeurà une variable. La variable est écrite à gauche et sa valeur à droite; les deux sont séparés par un caractère =.

Dans l'exemple ci-dessus, la variable est $aet la valeur est Velocity. Cette variable, comme toutes les références, commence par le caractère $. Les valeurs sont toujours entourées d'apostrophes; dans Velocity, il n'y a jamais de confusion possible entre les types de données puisque seules des chaînes de caractères (informations de type texte) peuvent être passées à des variables.

Le truc suivant peut être utile pour mieux comprendre comment Velocity fonctionne :Les références commencent par $ et sont utilisées pour récupérer quelque chose. Les directives commencent par #et sont utilisées pour faire quelque chose.

Dans l'exemple ci-dessus, #setest utilisé pour assigner une valeur à une variable. La variable, $a, peut alors être utilisée dans le gabarit pour produire "Velocity".

Hello Velocity World!

Une fois qu'une valeur a été assignée à une variable, vous pouvez faire référence à cette variable n'importe où dans votre document HTML. Dans l'exemple suivant, une valeur est assignée à $foo et référencée plus loin.

<html>
<body>
#set( $foo = "Velocity" )
Hello $foo World!
</body>
</html>

Le résultat est une page web où s'imprime "Hello Velocity World!".

Pour rendre les instructions contenant des directives VTL plus lisibles, nous vous encourageons à débuter chaque instruction VTL sur une nouvelle ligne -- mais vous n'êtes pas obligé de procéder ainsi. La directive set sera revue avec davantage de détails plus loin.

Les commentaires

Les commentaires permettent d'inclure du texte descriptif qui ne sera pas reporté dans la sortie du moteur d'inclusion. Les commentaires sont une manière utile de se rappeler et d'expliquer à d'autres ce que font les instructions VTL, ou à toute autre fin utile. Voici un exemple de commentaire en VTL.

## Ceci est un commentaire d'une seule ligne.

Un commentaire d'une ligne commence par ## et se termine à la fin de la ligne. Si vous voulez écrire quelques lignes de commentaires, pas besoin de multiplier ces commentaires d'une ligne. Les commentaires multi-lignes, qui commencent par #* et se terminent par *#, sont là pour ce cas de figure.

Ceci est du texte à l'extérieur du commentaire multi-lignes. Les visiteurs du site peuvent le voir.

#*
  Ici commence donc un commentaire de plusieurs lignes.
  Les visiteurs du site ne le verront pas, parce que le
  moteur de substitution de Velocity
  (Velocity Templating Engine) l'ignore.
*#

Ce texte-ci est à l'extérieur du commentaire: il est visible.

Voici quelques exemples pour clarifier la manière dont les commentaires d'une et plusieurs lignes fonctionnent:

Ce texte est visible. ## Ce texte ne l'est pas.
Ce texte est visible.
Ce texte est visible. #* Ce texte, qui fait partie d'un commentaire
de plusieurs lignes, n'est pas visible. Ce texte n'est pas visible;
il faut aussi partie du commentaire multi-lignes.
Ce texte n'est toujours pas visible. *# Ce texte est à l'extérieur
du commentaire, il est donc visible.
## Ce texte n'est pas visible.

Il y a un troisième type de commentaires, le bloc de commentaires VTL, que vous pouvez utiliser pour écrire des informations telles que l'auteur du document ou la version.

#**
Ceci est un bloc de commentaires VTL, qui
peut être utilisé pour inscrire des informations
telles que l'auteur ou la version du document.
@author
@version 5
*#

Les références

Il y a trois types de références en VTL: les variables, les propriétés et les méthodes. En tant que concepteur utilisant le VTL, vous et vos ingénieurs devez vous mettre d'accord sur les noms des références, de manière à pouvoir les utiliser correctement dans vos gabarits de pages.

Tout ce qui entre et sort d'une référence est traité comme un objet chaîne de caractères. S'il y a un objet qui représente $foo(un objet Integer par exemple), Velocity appellera sa méthode .toString() pour convertir l'objet en String.

Variables
La notation abrégée pour une variable consiste en un caractère "$" initial suivi d'un IdentificateurVTL. Un identificateur VTL doit commencer par une lettre (a .. z ou A .. Z). Le reste des caractères est limité aux types suivants:

  • lettre (a .. z, A .. Z)
  • chiffre (0 .. 9)
  • tiret ("-")
  • trait de soulignement ("_")

Voici quelques exemples de références valides en VTL:

$foo
$mudSlinger
$mud-slinger
$mud_slinger
$mudSlinger1

Lorsque VTL référence une variable, telle que $foo, la variable peut prendre sa valeur soit d'une directive set dans le gabarit, soit d'un programme Java. Par exemple, si la variable Java $foo a la valeur bar à la ligne à laquelle il est fait appel au gabarit, bar remplace toutes les instances de $foo dans la page web. Autrement, si j'inclus l'instruction

#set( $foo = "bar" )

la sortie sera la même pour toutes les instances de $foo qui suivent cette directive.

Propriétés
Les propriétés sont la seconde espèce de références en VTL et elles ont un format qui les distingue. La notation abrégée consiste en un caractère $ initial suivi d'un identifiant VTL, suivi d'un point (".") et d'un autre identifiant VTL. Quelques exemples de références valides de propriétés en VTL:

$customer.Address
$purchase.Total

Prenons le premier exemple, $customer.Address. Cette expression peut avoir deux significations. Elle peut signifier: "Regarde dans la table de hachage identifiée par customer et renvoie la valeur associée à la clé Address". Mais $customer.Address peut aussi faire référence à une méthode (les références qui désignent des méthodes seront discutées dans la section suivante); $customer.Address pourrait être une manière abrégée d'écrire $customer.getAddress(). Quand quelqu'un demande votre page, Velocity va déterminer laquelle de ces deux possibilités a un sens, et retournera la valeur appropriée.

Méthodes
Une méthode est définie dans le code Java et peut faire quelque chose d'utile, comme effectuer un calcul ou prendre une décision. Les méthodes sont des références qui consistent en un caractère "$" initial, suivi d'un identifiant VTL, suivi d'un corps de méthode. Un corps de méthode VTL consiste en un identifiant VTL suivi du caractère parenthèse ouvrante ("("), éventuellement suivi d'une liste de paramètres, suivi du caractère parenthèse fermante (")"). Quelques exemples de références de méthodes valides en VTL:

$customer.getAddress()
$purchase.getTotal()
$page.setTitle( "My Home Page" )
$person.setAttributes( ["Strange", "Weird", "Excited"] )

Les deux premiers exemples -- $customer.getAddress() et $purchase.getTotal() -- peuvent avoir l'air semblables à ceux utilisés dans la section précédente, consacrée aux Propriétés, $customer.Address et $purchase.Total. Si vous avez deviné que ces exemples sont liés entre eux d'une manière ou d'une autre, vous avez raison!

Les propriétés VTL peuvent être utilisées comme une notation abrégée pour des méthodes VTL. La propriété $customer.Address a exactement le même effet que l'utilisation de la méthode $customer.getAddress(). Il est généralement préférable d'utiliser une propriété lorsqu'il y en a une de disponible. La principale différence entre les Propriétés et les Méthodes est que pour les méthodes, on peut spécifier une liste de paramètres.

La notation abrégée peut être utilisée pour les méthodes suivantes

$sun.getPlanets()
$annelid.getDirt()
$album.getPhoto()

On s'attend logiquement à ce que ces méthodes retournent les noms des planètes qui tournent autour du soleil, qu'elles nourissent notre ver de terre ou prennent une photo dans un album. Il n'y a que la notation longue qui fonctionne pour les méthodes suivantes:

$sun.getPlanet( ["Earth", "Mars", "Neptune"] )
## On ne peut pas passer une liste de paramètres avec $sun.Planets

$sisyphus.pushRock()
## Velocity suppose que je veux dire $sisyphus.getRock()

$book.setTitle( "Homage to Catalonia" )
## On ne peut pas passer une liste de paramètres

Depuis Velocity 1.6, toutes les références à des tableaux sont "magiquement" considérées comme des listes de taille fixe. Cela signifie que vous pouvez appeler les méthodes de java.util.List sur les références contenant des tableaux. Ainsi, si vous avez une référence sur un tableau (mettons que celui-ci soit un tableau de String avec trois valeurs), vous pouvez écrire :

$myarray.isEmpty()

$myarray.size()

$myarray.get(2)

$myarray.set(1, 'test') 

Autre nouveauté de Velocity 1.6, la reconnaissance des méthodes à nombre variable d'arguments. Une méthode telle que public void setPlanets(String... planets) ou même simplement public void setPlanets(String[] planets) (si vous utilisez une version de Java antérieure au JDK5) peut maintenant accepter un nombre variable d'arguments quand elle est appelée depuis un gabarit.

Règles de recherche des méthodes
Comme mentionné plus haut, les propriétés font souvent référence à des méthodes de l'objet parent. Velocity est relativement malin quand il s'agit de déterminer à quelle méthode correspond une propriété demandée. Il essaye différentes alternatives basées sur plusieurs conventions de nommage établies. L'ordre exact de recherche dépend de ce que le nom la propriété commence ou non avec une majuscule. Pour des noms en minuscule, comme $customer.address, la séquence de recherche est :

  1. getaddress()
  2. getAddress()
  3. get("address")
  4. isAddress()
Pour des noms de propriété en majuscules, comme $customer.Address, c'est légèrement différent :
  1. getAddress()
  2. getaddress()
  3. get("Address")
  4. isAddress()

Rendu
La valeur finale résultant de toute référence (que ce soit une variable, une propriété ou une méthode) est convertie en objet String quand elle est intégrée à la sortie finale. S'il y a un objet qui représente $foo (tel qu'un objet Integer), alors Velocity appellera sa méthode .toString() pour résoudre l'objet en une chaîne de caractère.

Notation formelle des références
Dans les exemples ci-dessus, nous avons utilisé la notation abrégée pour les références, mais il y a aussi une notation formelle pour les références, illustrée ci-dessous:

${mudSlinger}
${customer.Address}
${purchase.getTotal()}

Dans presque tous les cas, vous utiliserez pour les références la notation abrégée, mais dans certains cas la notation formelle est requise pour une exécution correcte.

Supposons que vous soyez en train de construire dynamiquement une phrase dans laquelle $vice doit être utilisé comme base pour la construction d'un nom de la phrase. Le but est de permettre à quelqu'un de choisir le mot de base et de produire l'un des deux résultats suivants: "Jacques est pyromane" ou "Jacques est cleptomane". L'utilisation de la notation abrégée ne convient pas pour cette tâche. Considérons en effet l'exemple suivant:

Jacques est un $vicemane.

La syntaxe est ici ambiguë, et Velocity suppose que $vicemane, et non $vice, est l'identifiant que vous pensiez utiliser. Ne trouvant pas de valeur pour $vicemane, il renverra $vicemane. L'utilisation de la notation formelle peut résoudre ce problème.

Jacques est un ${vice}mane.

Cette fois Velocity sait que $vice, et non $vicemane, est la référence. La notation formelle est souvent utile quand les références sont directement adjacentes à du texte au sein d'un gabarit.

Notation silencieuse des références
Lorsque Velocity rencontre une référence non définie, son comportement normal est de produire une image de la référence. Par exemple, supposons que la référence suivante apparaisse dans un gabarit VTL:

<input type="text" name="email" value="$email"/>

Quand le formulaire est chargé pour la première fois, la variable référencée par $email n'a pas de valeur, mais vous préfèreriez un champ de texte vide à la valeur "$email". L'utilisation de la notation silencieuse contourne le comportement normal de Velocity; au lieu d'utiliser $email dans le code VTL, utilisez $!email. L'exemple précédent ressemblerait donc à ceci:

<input type="text" name="email" value="$!email"/>

A présent, quand le formulaire est chargé pour la première fois et que $email n'a toujours pas de valeur, une chaîne vide sera produite au lieu de "$email".

La notation formelle et la notation silencieuse peuvent être utilisées ensemble, comme illustré ci-dessous.

<input type="text" name="email" value="$!{email}"/>

Mode de notation stricte des références

Velocity 1.6 introduit le concept de références strictes, qui est activé en mettant à true le paramètre de configuration Velocity runtime.references.strict. Dans cette configuration les références doivent être soit placées explicitement dans le contexte, soit définies avec une directive #set, sinon Velocity jettera une exception. Les références qui sont dans le contexte avec une valeur nulle ne produiront pas d'exception. De plus, si une tentative est faite d'appeler une méthode ou une propriété sur un objet contenu dans une référence qui ne définit pas cette méthode ou cette propriété, alors Velocity jettera une exception. Cela reste vrai pour une tentative d'appeler une propriété ou une méthode sur une valeur nulle.

Dans les exemples suivants $bar est défini mais pas $foo, et toutes ces expressions jetteront une exception :

$foo ## Exception
#set($bar = $foo) ## Exception
#if($foo == $bar)#end ## Exception
#foreach($item in $foo)#end ## Exception

Les expressions suivantes montrent des exemples pour lesquels Velocity jettera une exception lors d'une tentative d'appel à des méthodes ou propriétés qui n'existent pas. Dans ces exemples, $bar contient un objet définissant la propriété foo qui renvoie une chaîne de caractères, et la propriété retnull qui renvoie null.

$bar.bogus ## $bar does not provide property bogus, Exception
$bar.foo.bogus ## $bar.foo does not provide property bogus, Exception
$bar.retnull.bogus ## cannot call a property on null, Exception

En général ce comportement de référence stricte est vrai pour toutes les situations dans lesquelles des références sont utilisées excepté dans un cas spécifique à l'intérieure de la directive #if. Si une référence est utilisée à l'intérieur d'une directive #if ou #elseif sans méthode ou propriété, et si elle n'est pas en train d'être comparée à une autre valeur, alors les références indéfinies sont autorisées. Ce comportement fournit un moyen simple de tester si une réference est définie avant de l'utiliser dans un gabarit. Dans l'exemple suivant, ou $foo n'est pas définit, les expressions ne produiront pas d'exceptions:

#if ($foo)#end ## False
#if ( ! $foo)#end ## True
#if ($foo && $foo.bar)#end ## False and $foo.bar will not be evaluated
#if ($foo && $foo == "bar")#end ## False and $foo == "bar" wil not be evaluated
#if ($foo1 || $foo2)#end ## False $foo1 and $foo2 are not defined

Une remarque supplémentaire : les références à des macros indéfinies jetteront également une exception.

Substitution de cas

Maintenant que les références vous sont familières, vous pouvez commencer à les mettre en pratique dans vos propres gabarits. Les références, dans Velocity, tirent avantage de certains principes Java que les concepteurs de gabarits vont trouver commodes. Par exemple:

$foo

$foo.getBar()
## est équivalent à
$foo.Bar

$data.setUser("jon")
## est équivalent à
#set( $data.User = "jon" )

$data.getRequest().getServerName()
## est équivalent à
$data.Request.ServerName
## est équivalent à
${data.Request.ServerName}

Ces exemples illustrent différentes manières d'utiliser les mêmes références. Velocity tire parti de l'introspection Java et des caractéristiques des beans pour résoudre les noms de référence en objets du contexte et aussi en leurs méthodes. Il est possible d'inclure et d'évaluer des références à peu près partout dans votre gabarit.

Velocity utilise comme modèles les spécifications des Beans telles qu'elles ont été définies par Sun Microsystems; il est donc sensible à la casse (majuscules/minuscules); les développeurs se sont toutefois battus pour intercepter et corriger les erreurs des utilisateurs chaque fois que c'est possible. Lorsque la méthode getFoo() est référencée dans un gabarit par $bar.foo, Velocity essayera d'abord $getfoo. Si ceci échoue, Velocity essayera $getFoo. De même, si un gabarit fait référence à $bar.Foo, Velocity essayera d'abord $getFoo() et ensuite getfoo().

Note: Dans un gabarit, les références à des variables d'instance ne sont pas résolues. Il n'y a que les références équivalentes aux attributs des accesseurs (getter / setter) des JavaBeans qui sont résolues (autrement dit $foo.Name est résolu par l'appel de la méthode d'instance getName() de la classe Foo, mais pas en une variable d'instance publique Name de cette classe Foo).

Directives

Les références permettent aux concepteurs de pages de générer du contenu dynamique pour des sites web alors que les directives -- des éléments de script, faciles à utiliser, qui peuvent être mis en oeuvre pour manipuler de manière créative la sortie d'un code Java -- permet aux concepteurs de vraiment prendre en charge l'apparence et le contenu du site web.

Les directives commencent toujours par un #. Comme les références, le nom des directives peut être entouré par des accolades { et }. C'est utile quand les directives sont immédiatement suivies par du texte. Par exemple, l'expression suivante produit une erreur :

#if($a==1)true ça va#elsepas question!#end

Dans ce cas, il faut utiliser des accolades pour séparer le #else du reste de la ligne :

#if($a==1)true ça va#{else}pas question!#end
#set

La directive #set s'utilise pour donner une valeur à une référence. Une valeur peut être assignée soit à une référence de type variable, soit à une référence de type propriété, et ceci toujours entre parenthèses, comme montré ici:

#set( $primate = "monkey" )
#set( $customer.Behavior = $primate )

Le côté gauche (left hand side -- LHS) de l'assignation doit être une référence variable ou propriété. Le membre de droite (right hand side -- RHS) peut être de l'un des types suivants:

  • Une variable
  • Une chaîne de caractères littérale
  • Une propriété
  • Une méthode
  • Un nombre littéral
  • Une liste
  • Un dictionnaire

Les exemples suivants montrent chacun des types susmentionnés:

#set( $monkey = $bill ) ## variable
#set( $monkey.Friend = "monica" ) ## chaîne de caractères littérale
#set( $monkey.Blame = $whitehouse.Leak ) ## propriété
#set( $monkey.Plan = $spindoctor.weave($web) ) ## méthode
#set( $monkey.Number = 123 ) ## nombre littéral
#set( $monkey.Say = ["Not", $my, "fault"] ) ## liste
#set( $monkey.Map = {"banana" : "good", "roast beef" : "bad"}) ## dictionnaire

NOTE: Pour l'exemple de la liste, les éléments définis avec l'opérateur [..] sont accessibles en utilisant les méthodes définies dans la classe ArrayList. Ainsi, par exemple, on peut accéder au premier élément en écrivant $monkey.Say.get(0)

De même, pour l'exemple du dictionnaire, les éléments définis avec l'opérateur { } sont accessibles en utilisant les méthodes définies dans la classe Map. Ainsi, par exemple, on peut accéder au premier élément en écrivant $monkey.Map.get("banana") pour retourner la chaîne 'good', ou même $monkey.Map.banana pour retourner la même valeur.

Le membre de droite peut aussi être une expression arithmétique simple:

#set( $value = $foo + 1 )
#set( $value = $bar - 1 )
#set( $value = $foo * $bar )
#set( $value = $foo / $bar )

Si le membre de droite est une référence à une méthode ou à une propriété dont la valeur est null, il ne sera pas affecté au membre de gauche. Il n'est pas possible d'enlever une référence existante du contexte par ce biais-là. Ceci peut troubler les débutants en Velocity. Par exemple:

#set( $result = $query.criteria("name") )
Le résultat de la première requête est $result

#set( $result = $query.criteria("address") )
Le résultat de la seconde requête est $result

Si $query.criteria("name") renvoie la chaîne "bill", et que $query.criteria("address") renvoie null, le code VTL ci-dessus sera rendu de la manière suivante:

Le résultat de la première requête est bill

Le résultat de la seconde requête est bill

Ceci induit en confusion les nouveaux venus, qui construisent des boucles #foreach qui tentent de faire un #set sur une référence à partir d'une référence à une propriété ou une méthode et testent immédiatement cette référence avec une directive #if. Par exemple:

#set( $criteria = ["name", "address"] )

#foreach( $criterion in $criteria )

    #set( $result = $query.criteria($criterion) )

    #if( $result )
        Query was successful
    #end

#end

Dans l'exemple ci-dessus, il ne serait pas avisé de se reposer sur l'évaluation de $result pour déterminer si une requête a été couronnée de succès. Une fois que $result a reçu une valeur par un #set (et a donc été ajouté au contexte), il ne peut pas recevoir la valeur null (et se trouver ainsi enlevé du contexte). Les détails des directives #if et #foreach sont traités plus loin dans ce document.

Une solution à ce problème serait de pré-positionner $result à false. Si l'appel à $query.criteria() échoue, il est possible de faire la vérification.

#set( $criteria = ["name", "address"] )

#foreach( $criterion in $criteria )

    #set( $result = false )
    #set( $result = $query.criteria($criterion) )

    #if( $result )
        La requête a abouti correctement
    #end

#end

Contrairement à d'autres directives Velocity, la directive #set n'a pas d'instruction #end.

Chaînes de caractères littérales

Lorsque vous utilisez la directive #set, les chaînes de caractères littérales délimitées par des guillements sont interprétées et rendues de la manière suivante:

#set( $directoryRoot = "www" )
#set( $templateName = "index.vm" )
#set( $template = "$directoryRoot/$templateName" )
$template

La sortie produite sera

www/index.vm

Toutefois, lorsque la chaîne de caractères littérale est délimitée par des apostrophes, elle n'est pas interprétée.

#set( $foo = "bar" )
$foo
#set( $blargh = '$foo' )
$blargh

Ce qui est rendu de la manière suivante:

  bar
  $foo

Cette caractéristique d'utilisation des apostrophes pour rendre du texte non interprété est le comportement par défaut de Velocity. Ce comportement peut être changé en éditant le fichier velocity.properties et en y écrivant l'entrée: stringliterals.interpolate=false.

Également, la directive #[[ ]]# permet au concepteur de gabarits de facilement utiliser de gros morceaux de contenu non interprété de code VTL. Ceci peut être particulièrement utile en remplacement de multiples échappements de directives.

#[[
#foreach ($woogie in $boogie)
  nothing will happen to $woogie
#end
]]# 

Sera rendu comme :

#foreach ($woogie in $boogie)
  nothing will happen to $woogie
#end

Conditions

If / ElseIf / Else

La directive #if de Velocity permet à du texte d'être inclus à la génération d'une page web seulement si la condition qui suit l'instruction if est vérifiée. Par exemple:

#if( $foo )
   <strong>Velocity!</strong>
#end

La variable $foo est évaluée pour déterminer si elle vaut true, ce qui se produit dans l'une des deux cas suivants; (i) $foo est une variable booléenne (true/false) dont la valeur est vrai (true), ou (ii) la valeur de $foo est différente de null. On se rappelle que le contexte de Velocity ne contient que des Objets, et donc lorsqu'on dit un booléen, il sera représenté comme un objet de la classe Boolean contenant la valeur logique appropriée.

Ce qui est contenu entre l'instruction #if et l'instruction #end sera produit en sortie si la condition est évaluée comme vraie. Dans l'exemple précédent, si $foo est true, la sortie sera: "Velocity!". A l'inverse, si $foo a la valeur null, ou si c'est un booléen de valeur false, l'instruction est évaluée comme fausse, et il n'y a pas de sortie produite.

Un élément #elseif ou #else peut être utilisé dans la directive #if. Notez que le moteur Velocity (Velocity Template Engine) s'arrêtera à la première expression évaluée comme vraie. Dans l'exemple suivant, supposons que $foo vaut 15 et que $bar vaut 6.

#if( $foo < 10 )
    <strong>Go North</strong>
#elseif( $foo == 10 )
    <strong>Go East</strong>
#elseif( $bar == 6 )
    <strong>Go South</strong>
#else
    <strong>Go West</strong>
#end

Dans cet exemple, $foo est plus grand que 10, donc les deux premières comparaisons échouent. Ensuite, $bar est comparé à 6, ce qui donne vrai, et dont la sortie produit est Go South.

Opérateurs logiques et relationnels

Velocity utilise l'opérateur d'équivalence pour déterminer les relations entre des variables. Voici un exemple simple pour illustrer la manière d'utiliser l'opérateur d'équivalence.

#set ($foo = "deoxyribonucleic acid")
#set ($bar = "ribonucleic acid")

#if ($foo == $bar)
  Dans ce cas, il est clair qu'ils ne sont pas équivalents. Donc...
#else
  Ils ne sont pas équivalents et c'est ceci qui sera produit en sortie.
#end

Velocity a aussi des opérateurs ET, OU et NON. Pour plus d'informations, référez-vous au VTL Reference Guide (en anglais). Ci-dessous, quelques exemples illustrent l'utilisation des opérateurs logiques ET, OU et NON.

##ET logique

#if( $foo && $bar )
   <strong> Ceci ET cela.</strong>
#end

La directive #if() ne sera évaluée comme true que si $foo et $bar sont true tous les deux. Si $foo est false, l'expression sera globalement évaluée comme false et $bar ne sera pas évalué. Si $foo vaut true, le moteur Velocity testera la valeur de $bar; si $bar vaut true, alors l'expression dans son entièreté vaut true et la sortie Ceci ET cela est produite. Si $bar est false, alors il n'y aura pas de sortie produite puisque l'expression entière est fausse.

Les opérateurs logiques OU fonctionnent de la même manière, si ce n'est qu'une seule des références doit être évaluée à true pour que l'expression entière soit considérée comme vraie. Voyez l'exemple suivant:

##OU logique

#if( $foo || $bar )
    <strong>Ceci OU cela</strong>
#end

Si $foo vaut true, le moteur Velocity n'a pas besoin d'évaluer $bar; que $bar soit vrai ou faux ne change rien à l'affaire, l'expression sera vraie, et Ceci OU cela sera produit en sortie. Mais si $foo est false, la valeur de $bar doit être vérifiée. Dans ce cas, si $bar est faux lui aussi, l'expression sera fausse et il n'y aura pas de sortie produite. Sinon, si $bar est vrai, alors l'expression entière est vraie, et la sortie est Ceci OU cela.

Avec l'opérateur logique NON, il n'y a qu'un seul argument :

##NON logique

#if( !$foo )
  <strong>PAS ça</strong>
#end

Cette fois, si $foo vaut true, alors !$foo est évalué comme false, et il n'y a pas de sortie. Si $foo est false, alors !$foo est évalué à true et PAS ça est produit en sortie. Attention à ne pas confondre ceci avec la référence silencieuse $!foo que nous avons déjà rencontrée et qui représente quelque chose de complètement différent.

Il y a des versions textes de tous les opérateurs logiques, incluant eq (égal), ne (différent), and (et), or (ou), not (négation), gt (strictement plus grand), ge (plus grand ou égal), lt (strictement plus petit) et le (plus petit ou égal).

Une dernière remarque. Quand on souhaite inclure du texte immédiatement après une directive #else, on doit utiliser des accolades directement autour de la directive pour la différencier du texte qui suit (toutes les directives peuvent être délimitées avec des accolades, même si c'est principalement utile avec #else).

#if( $foo == $bar )C'est ça !#{else}C'est pas ça!#end

Boucles

Boucle Foreach

L'élément #foreach permet d'itérer. Par exemple:

<ul>
#foreach( $product in $allProducts )
    <li>$product</li>
#end
</ul>

Cette boucle #foreach parcourt un à un tous les produits (cibles) de la liste $allProducts (objet). A chaque passage dans la boucle, une valeur de $allProducts est placée dans la variable $product.

Le contenu de la variable $allProducts est un vecteur (Vector), une table de hachage (Hashtable) ou un tableau (Array). La valeur assignée à $product est un objet Java, et peut être référencée en tant que telle par une variable. Par exemple, si $product est en effet une classe Product en Java, son nom peut être récupéré en référençant la méthode $product.Name (c'est-à-dire $Product.getName()).

Supposons que $allProducts soit une Hashtable. Si vous voulez retrouver les valeurs des clés et les objets de la Hashtable, vous pouvez utiliser un bout de code comme celui-ci:

<ul>
#foreach( $key in $allProducts.keySet() )
    <li>Key: $key -> Value: $allProducts.get($key)</li>
#end
</ul>

Velocity fournit un moyen simple de connaître le compteur de boucle, de sorte qu'on puisse faire quelque chose comme:

<table>
#foreach( $customer in $customerList )
    <tr><td>$foreach.count</td><td>$customer.Name</td></tr>
#end
</table>

Velocity fournit aussi un moyen simple de savoir si l'on est sur la dernière itération d'une boucle :

#foreach( $customer in $customerList )
  $customer.Name #if( $velocityHasNext ),#end
#end   

Il est possible de définir le maximum autorisé du nombre de fois qu'une boucle peut être exécutée. Par défaut il n'y a pas de maximum (c'est indiqué par la valeur 0 ou moins), mais on peut le fixer à un nombre arbitraire dans le fichier velocity.properties. Cela peut être utile comme garde-fou.

# The maximum allowed number of loops.
directive.foreach.maxloops = -1 

Si l'on veut arrêter de boucler depuis l'intérieur d'un #foreach, on peut maintenant utiliser la directive #break à n'importe quelle itération de la boucle :

## list first 5 customers only
#foreach( $customer in $customerList )
  #if( $foreach.count > 5 )
    #break
  #end
  $customer.Name
#end 

Include

L'élément de script #include permet au concepteur de gabarits d'importer un fichier local, qui est alors inséré à l'endroit où la directive #include est définie. Le contenu du fichier n'est pas rendu en passant par le moteur de substitution. Pour des raisons de sécurité, le fichier à inclure ne peut se trouver que sous TEMPLATE_ROOT.

#include( "one.txt" )

Le fichier auquel la directive #include fait référence est inclus entre des guillemets. Si plusieurs fichiers doivent être inclus, leurs noms doivent être séparés par des virgules.

#include( "one.gif","two.txt","three.htm" )

Le fichier à inclure ne doit pas nécessairement être appelé par son nom; en fait, il est souvent préférable d'utiliser une variable plutôt qu'un nom de fichier. Ce qui peut être utile pour cibler ce qui est produit en fonction de critères déterminés au moment où la page est demandée. Voici un exemple qui utilise à la fois un nom de fichier et une variable.

#include( "greetings.txt", $seasonalstock )

Parse

L'élément de script #parse permet au concepteur de gabarits d'importer un fichier local contenant du VTL. Velocity va alors interpréter le VTL et rendre le gabarit spécifié.

#parse( "me.vm" )

Comme la directive #include, #parse peut prendre en argument une variable plutôt qu'un nom de gabarit. Tous les gabarits auxquels il est fait référence par #parse doivent se trouver sous TEMPLATE_ROOT. Contrairement à la directive #include, #parse ne peut prendre qu'un seul argument.

Les gabarits VTL peuvent contenir des instructions #parse faisant référence à des gabarits qui à leur tour contiennent des #parse. Par défaut à 10, la ligne parse_directive.maxdepth du fichier velocity.properties permet aux utilisateurs de personnaliser le nombre de références #parse que l'on peut rencontrer dans un gabarit. (Note: Si la propriété parse_directive.maxdepth est absente du fichier velocity.properties, Velocité positionne cette valeur par défaut à 10). La récursion est permise; par exemple si le gabarit dofoo.vm contient les lignes suivantes:

Count down.
#set( $count = 8 )
#parse( "parsefoo.vm" )
All done with dofoo.vm!

Il fait référence au gabarit parsefoo.vm, qui peut contenir le VTL suivant:

$count
#set( $count = $count - 1 )
#if( $count > 0 )
    #parse( "parsefoo.vm" )
#else
    All done with parsefoo.vm!
#end

Après que "Count down." soit affiché, Velocity passe par parsefoo.vm, comptant à rebours à partir de 8. Lorsque le compte à rebours atteint 0, il affiche le message "All done with parsefoo.vm!". A ce stade, Velocity va retourner à dofoo.vm et produire le message "All done with dofoo.vm!".

Stop

L'élément de script #stop permet au concepteur de gabarits d'arrêter l'exécution du moteur de substitution. Cette directive peut être utile pour le débogage.

#stop

Evaluate

La directive #evaluate peut être utilisée pour évaluer dynamiquement du code VTL. Cela permet au gabarit d'évaluer une chaîne qui est construite au moment du rendu. Une telle chaîne pourrait être utilisée pour internationaliser le gabarit ou pour include des fragments de gabarit depuis une base de donnée.

L'exemple ci-dessous affiche abs

#set($source1 = "abc")
#set($select = "1")
#set($dynamicsource = "$source$select")
## $dynamicsource is now the string '$source1'
#evaluate($dynamicsource) 

Define

La directive #define permet d'assigner un bloc de VTL à une référence.

L'exemple ci-dessous affiche Hello World!

#define( $block )Hello $who#end
#set( $who = 'World!' )
$block 

Velocimacros

L'élément de script #macro permet aux concepteurs de définir un segment répétable d'un gabarit VTL. Les "Velocimacros" sont très utiles dans un grand nombre de scénarios, simples ou complexes. Une Velocimacro, écrite dans le seul but de s'économiser un peu de frappe et de minimiser les fautes, servira d'introduction au concept de Velocimacro.

#macro( d )
<tr><td></td></tr>
#end

La Velocimacro définie dans cet exemple est d; elle peut être appelée d'une manière semblable à toute autre directive VTL:

#d()

Lorsque ce gabarit est appelé, Velocity remplace #d() par une ligne contenant une cellule de données vide.

Une Velocimacro peut prendre n'importe quel nombre d'arguments -- même zéro, comme on l'a vu dans l'exemple -- mais lorsque la macro est appelée, elle doit l'être avec le même nombre d'arguments que dans la définition. Beaucoup de Velocimacros sont plus sophistiquées que celle définie ci-dessus; voici une Velocimacro qui prend deux arguments, une couleur et un tableau.

#macro( tablerows $color $somelist )
#foreach( $something in $somelist )
    <tr><td bgcolor=$color>$something</td></tr>
#end
#end

La Velocimacro définie dans cet exemple, tablerows, prend deux arguments. Le premier argument prend la place de $color et le second argument prend la place de $somelist.

Tout ce qui peut être mis dans un gabarit VTL peut aussi trouver place dans le corps d'une Velocimacro. La Velocimacro tablerows contient une instruction foreach. On remarquera qu'il y a deux instructions #end dans la définition de la Velocimacro #tablerows; la première termine le #foreach, la seconde termine la définition de la Velocimacro.

#set( $greatlakes = ["Superior","Michigan","Huron","Erie","Ontario"] )
#set( $color = "blue" )
<table>
    #tablerows( $color $greatlakes )
</table>

Notez que $greatlakes prend la place de $somelist. Quand la Velocimacro #tablerows est appelée dans ce contexte, la sortie suivante est produite:

<table>
    <tr><td bgcolor="blue">Superior</td></tr>
    <tr><td bgcolor="blue">Michigan</td></tr>
    <tr><td bgcolor="blue">Huron</td></tr>
    <tr><td bgcolor="blue">Erie</td></tr>
    <tr><td bgcolor="blue">Ontario</td></tr>
</table>

Les Velocimacros peuvent être définies inline dans un gabarit Velocity; elles ne sont pas disponibles dans ce cas pour d'autres gabarits Velocity sur le même site web. Définir une Velocimacro pour qu'elle puisse être partagée par tous les gabarits a des avantages évidents: cela réduit le besoin de redéfinir la Velocimacro dans de nombreux gabarits, cela économise du travail et réduit les occasions de se tromper, cela assure qu'un chagement fait une seule fois dans une macro est aussitôt disponible dans tous les gabarits.

Si la Velocimacro #tablerows($color $list) avait été définie dans une bibliothèque de gabarits Velocimacros, cette macro aurait pu être utilisée dans n'importe lequel des gabarits usuels. Elle pourrait être utilisée de nombreuses fois et dans des buts différents. Dans le gabarit mushroom.vm consacré à toute espèce de champignons, la Velocimacro #tablerows pourrait être appelée pour donner la liste des parties d'un champignon typique.

#set( $parts = ["volva","stipe","annulus","gills","pileus"] )
#set( $cellbgcol = "#CC00FF" )
<table>
#tablerows( $cellbgcol $parts )
</table>

Lors de l'exécution d'une requête pour mushroom.vm, Velocity trouverait la Velocimacro #tablerows dans la bibliothèque de gabarits (définie dans le fichier velocity.properties) et produirait la sortie suivante:

<table>
    <tr><td bgcolor="#CC00FF">volva</td></tr>
    <tr><td bgcolor="#CC00FF">stipe</td></tr>
    <tr><td bgcolor="#CC00FF">annulus</td></tr>
    <tr><td bgcolor="#CC00FF">gills</td></tr>
    <tr><td bgcolor="#CC00FF">pileus</td></tr>
</table>
Arguments d'une Velocimacro

Les Velocimacros peuvent prendre comme argument tout élément VTL parmi les suivants:

  • Référence: tout ce qui commence par '$'
  • Chaîne de caractères littérale: quelque chose comme "$foo" ou 'hello'
  • Nombre littéral: 1, 2 etc
  • Intervalle d'entiers (IntegerRange) : [ 1..2] ou [$foo .. $bar]
  • ObjectArray : [ "a", "b", "c"]
  • Valeur booléenne true
  • Valeur booléenne false

Lorsqu'on passe des références comme arguments à des Velocimacros, notez que ces références sont passées "par nom". Ce qui veut dire que leur valeur est "générée" à chaque utilisation à l'intérieur d'une Velocimacro. Cette particularité vous permet de passer des références contenant des appels de méthodes et que la méthode soit appelée à chaque fois. Par exemple, en appelant la Velocimacro suivante comme indiqué:

     #macro( callme $a )
         $a $a $a
     #end

     #callme( $foo.bar() )
   

le résultat est que la méthode bar() de la référence $foo est appelée trois fois.

A première vue, cette particularité est surprenante mais si vous considérez la raison d'être originelle des Velocimacros (éliminer la duplication par couper/coller de VTL d'usage courant), ça a bien sûr un sens. Cela vous permet de faire des choses étonnantes telles que passer à la Velocimacro des objets ayant un état, comme un objet qui génère des couleurs en séquences répétées pour colorer les lignes d'un tableau.

Si vous éprouvez le besoin de contourner ce comportement, vous pouvez toujours assigner la valeur reçue de la méthode à une nouvelle référence et passer cette référence:

     #set( $myval = $foo.bar() )
     #callme( $myval )
  
Propriétés des Velocimacros

Plusieurs entrées du fichier velocity.properties permettent une implémentation flexible des Velocimacros. Ces lignes sont commentées en détail dans le Developer Guide (en anglais).

velocimacro.library - Une liste (délimitée par des virgules, de toutes les bibliothèques de gabarits Velocimacro. Par défaut, Velocity ne recherche qu'une bibliothèque: VM_global_library.vm. Le chemin de gabarits donné ici est utilisé pour trouver les bibliothèques de Velocimacros.

velocimacro.permissions.allow.inline - Cette propriété, qui peut prendre les valeurs true ou false, détermine si les Velocimacros peuvent être définies dans des gabarits ordinaires. La valeur par défaut, true, permet aux concepteurs de gabarits de définir des Velocimacros dans les gabarits eux-mêmes.

velocimacro.permissions.allow.inline.to.replace.global - Les valeurs possibles sont true ou false, pour cette propriété qui permet à l'utilisateur de spécifier si une Velocimacro définie en ligne dans un gabarit peut remplacer un gabarit défini globalement, celui qui a été défini au démarrage par la propriété velocimacro.library. La valeur par défaut, false, empêche les Velocimacros défines inline dans un gabarit de remplacer celles qui sont définies dans les bibliothèques de gabarits chargées au démarrage.

velocimacro.permissions.allow.inline.local.scope - Cette propriété, qui peut prendre les valeurs true ou false (false par défaut), contrôle si les Velocimacros définies inline ont leur visibilité limitée au gabarit qui les définit. En d'autres mots, avec cette propriété définie à true, un gabarit peut définir des Velocimacros inline qui ne seront utilisables que par le gabarit qui les définit. Vous pouvez utiliser cette possibilité pour concocter quelques artifices amusants: si une VM globale appelle une autre VM globale, définie inline, un gabarit peut définir une implémentation "privée" de la seconde VM, qui sera appellée par la première VM quand celle-ci est appelée elle-même dans le gabarit. Aucun autre gabarit n'est affecté.

velocimacro.library.autoreload - Cette propriété contrôle le chargement automatique de la bibliothèque de Velocimacro. La valeur par défaut est false. Quand elle est positionnée à true la bibliothèque source appelée pour une Velocimacro sera vérifiée pour voir si elle a changé et sera rechargée si nécessaire. Ceci vous permet de modifier et de tester des bibliothèques de Velocimacro sans avoir à redémarrer votre application ou votre moteur de servlets, exactement comme vous pouvez le faire pour des gabarits ordinaires. Ce mode ne fonctionne que quand le cache est désactivé dans les resource loaders (par exemple file.resource.loader.cache = false). Cette possibilité a été conçue pour le développement, pas pour la production.

Autres remarques sur les Velocimacros

A ce stade, les Velocimacros doivent être définies avant d'être utilisées dans un gabarit. Cela signifie que vos déclarations #macro() doivent précéder l'usage des Velocimacros.

Il est important de s'en souvenir si l'on essaye d'interpréter (#parse()) un gabarit contenant des directives #macro() inline. Puisque l'interprétation se fait au moment de l'exécution, et que l'interpréteur décide au moment de l'interprétation si, dans un gabarit, un élément qui a l'air d'une VM en est vraiment une, #parse()-er un ensemble de déclarations de VM ne produira pas le résultat escompté. Pour contourner ce problème potentiel, on peut utiliser la propriété velocimacro.library pour que Velocity charge vos VMs au démarrage.

Sortie littérale

VTL utilise des caractères spéciaux, comme $ et # pour accomplir sa tâche; il faut donc prendre quelques précautions pour utiliser ces caractères dans vos gabarits. Cette section est consacrée à l'échappement du caractère $.

Devise
Il n'y a pas de problème particulier à écrire "J'ai acheté un sac de 2 kg de patates au marché de la ferme pour seulement $2.50!". Comme dit plus haut, un identifiant VTL commence toujours par une lettre, majuscule ou minuscule, et donc $2.50 ne serait pas pris par erreur pour une référence.

Echapper des références VTL valides
Il peut se produire dans certains cas que Velocity se trouve induit en confusion. Echapper les caractères spéciaux est le meilleur moyen de traiter les caractères spéciaux du VTL dans vos gabarits, et ceci se fait en utilisant le caractère backslash (\).

#set( $email = "foo" )
$email

Lorsque Velocity rencontre une référence à $email dans votre gabarit VTL, il cherche dans le contexte la valeur correspondante. Ici, la sortie sera foo, parce que $email est défini. Si $email n'était pas défini, la sortie serait $email.

Supposons que $email soit défini (par exemple, cette référence a la valeur foo) et que vous vouliez produire $email. Il y a différentes manières de le faire, mais la plus simple est d'utiliser le caractère d'échappement.

## La ligne qui suit définit $email dans ce gabarit:
#set( $email = "foo" )
$email
\$email
\\$email
\\\$email

sera rendu comme:

foo
$email
\foo
\$email

Notez que le caractère \ s'applique au $ à partir de la gauche. Cette règle d'application à partir de la gauche fait que \\\$email est rendu comme \\$email. Comparons maintenant ces examples à ce qui se passe lorsque $email n'est pas défini.

$email
\$email
\\$email
\\\$email

sera rendu comme:

$email
\$email
\\$email
\\\$email

Remarquez comment Velocity traite les références définies différemment de celles qui n'ont pas été définies. Voici par exemple une directive set qui donne à $foo la valeur gibbous.

#set( $foo = "gibbous" )
$moon = $foo

La sortie sera: $moon = gibbous -- où $moon est rendu littéralement puisqu'il n'est pas défini, alors que gibbous est rendu au lieu de $foo.

Echapper des références VTL valides
Velocity a parfois des problèmes à interpréter un gabarit quand il rencontre une "référence invalide" pour du texte dont on n'a jamais souhaité que ce soit une référence. Échapper les caractères spéciaux est de nouveau le meilleur moyen de gérer ces situations, mais le caractère \ risque de ne pas fonctionner dans ce cas. Au lieu d'essayer de juste échapper le caractère $ ou # problématique, il faut probabement simplement remplacer :

${my:invalid:non:reference} 

Par :

#set( $D = '$' )
${D}{my:invalid:non:reference}

Vous pouvez bien sûr mettre vos chaînes $ et # directement dans le contexte depuis votre code Java (i.e. context.put("D","$");) pour éviter les directives #set additionnelles dans vos gabarits. Ou, si vous utilisez VelocityTools, vous pouvez tout simplement utiliser l'outil EscapeTool comme suit :

${esc.d}{my:invalid:non:reference} 

Echappement des directives VTL

Les directives VTL peuvent être échappées avec le caractère barre de fraction inversée ("\"), de la même manière que les références VTL valides.

## #include( "a.txt" ) renders as <contents of a.txt>
#include( "a.txt" )

## \#include( "a.txt" ) renders as #include( "a.txt" )
\#include( "a.txt" )

## \\#include ( "a.txt" ) renders as \<contents of a.txt>
\\#include ( "a.txt" )

Il faut prendre des précautions particulières lorsqu'on échappe des directives VTL qui contiennent plusieurs éléments de script en une seule directive (comme dans le cas d'une instruction conditionnelle if-else-end). Voici un exemple typique d'instruction VTL if:

#if( $jazz )
    Vyacheslav Ganelin
#end

Si $jazz est vrai, la sortie produite est:

Vyacheslav Ganelin

Si $jazz, il n'y a pas de sortie produite. Echapper des éléments de script modifie la sortie. Considérons le cas suivant:

\#if( $jazz )
    Vyacheslav Ganelin
\#end

Que $jazz soit vrai ou faux, la sortie sera:

 #if($ jazz )
     Vyacheslav Ganelin
 #end

En fait, puisque tous les éléments de script sont échappés, $jazz n'est jamais évalué en vue d'en connaître la valeur logique. Supposons que les barres de fraction inverses précèdent les éléments de script qui sont légitimement échappés:

\\#if( $jazz )
   Vyacheslav Ganelin
\\#end

Dans ce cas, si $jazz est vrai, la sortie est

\ Vyacheslav Ganelin
\

Pour comprendre, notons que le #if( arg ) , lorsqu'il est terminé par un retour à la ligne, omettra ce retour à la ligne de la sortie produite. Donc, le corps du bloc #if() suit la première barre '\', rendue par le '\\' qui précède #if(). Le dernier \ est sur une ligne différente du texte qui précède parce qu'il y a un retour chariot après 'Ganelin', et donc le \\ final, qui précède le #end fait partie du corps du bloc.

Si $jazz est faux, la sortie est

\

Notons que les choses vont commencer à mal se passer si des éléments de script ne sont pas échappés correctement.

\\\#if( $jazz )
    Vyacheslave Ganelin
\\#end

Ici le #if est échappé, mais il y a un #end qui reste là, et trop de terminaisons vont causer une erreur dans l'interprétation.

VTL: Questions de format

Bien que le VTL soit souvent montré dans ce guide de l'utilisateur avec des sauts de ligne et des espaces, le VTL ci-dessous:

#set( $imperial = ["Munetaka","Koreyasu","Hisakira","Morikune"] )
#foreach( $shogun in $imperial )
    $shogun
#end

est tout aussi valide que le bout de code suivant, posté par Geir Magnusson Jr. à la liste de diffusion Velocity (pour illustrer un point sans rapport avec notre sujet):

Send me #set($foo = ["$10 and ","a cake"])#foreach($a in $foo)$a #end please.

Velocity digère les blancs inutiles. La directive qui précède peut donc s'écrire de la manière suivante:

Send me
#set( $foo = ["$10 and ","a cake"] )
#foreach( $a in $foo )
$a
#end
please.

ou encore:

Send me
#set($foo       = ["$10 and ","a cake"])
                 #foreach           ($a in $foo )$a
         #end please.

Dans tous les cas, la sortie sera identique.

Autres caractéristiques et sujets divers

Math

Velocity a quelques fonctions mathématiques intégrées, fonctions que l'on peut utiliser dans les gabarits avec la directive set. Les équations suivantes sont des exemples d'addition, soustraction, multiplication et division respectivement:

#set( $foo = $bar + 3 )
#set( $foo = $bar - 4 )
#set( $foo = $bar * 6 )
#set( $foo = $bar / 2 )

Lorsqu'une opération de division est exécutée entre deux entiers, le résultat sera un entier. Le reste éventuel de la division peut être obtenu en utilisant l'opérateur modulo (%).

#set( $foo = $bar % 5 )

Opérateur d'intervalle (range)

L'opérateur d'intervalle peut être utilisé en conjonction avec les instructions #set et #foreach. C'est assez commode pour produire un tableau d'objets contenant des entiers; l'opérateur d'intervalle est construit comme ceci:

[n..m]

n et m doivent tous deux être ou renvoyer des entiers. Que m soit plus grand ou plus petit que n importe peu, si cela se produit le tableau décompte. Voici quelques exemples de l'opérateur de portée:

Premier exemple:
#foreach( $foo in [1..5] )
$foo
#end

Second exemple:
#foreach( $bar in [2..-2] )
$bar
#end

Troisième exemple:
#set( $arr = [0..1] )
#foreach( $i in $arr )
$i
#end

Quatrième exemple:
[1..3]

Produit la sortie suivante:

Premier exemple:
1 2 3 4 5

Second exemple:
2 1 0 -1 -2

Troisième exemple:
0 1

Quatrième exemple:
[1..3]

Il est à noter que l'opérateur d'intervalle ne produit le tableau qu'utilisé en conjonction avec les directives #set et #foreach, comme démontré dans le quatrième exemple.

Les concepteurs de pages web soucieux de construire des tables de taille standard, mais dans lequelles il n'y a pas assez de données pour remplir la table, trouveront cet opérateur d'intervalle particulièrement utile.

Questions pointues: Echappement et !

Lorsqu'une référence est annulée par le caractère ! et que ce caractère ! est précédé par un caractère d'échappement \, la référence est traitée de manière particulière. A noter: les différences entre l'échappement ordinaire et le cas particulier où \ précède ! comme ici:

#set( $foo = "bar" )
$\!foo
$\!{foo}
$\\!foo
$\\\!foo

Ceci produit la sortie suivante:

$!foo
$!{foo}
$\!foo
$\\!foo

A comparer avec l'échappement ordinaire, où \ précède $:

\$foo
\$!foo
\$!{foo}
\\$!{foo}

Ce qui produit la sortie suivante:

$foo
$!foo
$!{foo}
\bar

Compléments divers sur les Velocimacros

Cette section est une mini-FAQ sur différents sujets relatifs aux Velocimacros. Cette section évoluera au fil du temps, et cela vaudra donc la peine d'y revenir occasionnellement pour y chercher de nouvelles informations.

Note: tout au long de cette section, 'Velocimacro' sera habituellement abrégé en 'VM'.

Puis-je utiliser une directive ou une autre VM comme argument d'une VM?

Exemple : #center( #bold("hello") )

Non. Une directive n'est pas un argument valide d'une directive, et en pratique, pour l'essentiel, une VM est une directive.

Pourtant..., il y a des choses que vous pouvez faire. Une solution, simple, est de tirer avantage du fait que l'apostrophe (") restitue son contenu. Vous pouvez donc écrire quelque chose comme

#set($stuff = "#bold('hello')" )
#center( $stuff )

Vous pouvez même vous épargner une étape...

#center( "#bold( 'hello' )" )

Mais il est à noter, dans ce dernier exemple, que l'argument est évalué à l'intérieur de la VM, pas au niveau de l'appel. En d'autres termes, l'argument passé à la VM est passé dans son intégralité et évalué dans la VM à laquelle il a été passé. Ceci permet d'écrire des choses comme:

#macro( inner $foo )
  inner : $foo
#end

#macro( outer $foo )
   #set($bar = "outerlala")
   outer : $foo
#end

#set($bar = 'calltimelala')
#outer( "#inner($bar)" )

La sortie produite est

Outer : inner : outerlala

puisque l'évaluation de "#inner($bar)" se produit à l'intérieur de #outer(), et c'est donc la valeur $bar positionnée à l'intérieur de #outer() qui est utilisée.

Ceci est tout à fait intentionnel, et c'est une caractéristique jalousement gardée - les arguments sont passés 'par nom' aux VM, de sorte qu'on puisse passer aux VM un genre de "références avec état" telles que

#macro( foo $color )
  <tr bgcolor=$color><td>Hi</td></tr>
  <tr bgcolor=$color><td>There</td></tr>
#end

#foo( $bar.rowColor() )

et appeler rowColor() plusieurs fois, plutôt qu'une fois. Pour éviter cela, il faut appeler la méthode hors de la VM et passer la valeur à la VM.

#set($color = $bar.rowColor())
#foo( $color )
Peut-on enregistrer des Velocimacros avec #parse() ?

Oui ! C'est devenu possible avec Velocity 1.6.

Si vous utilisez un version antérieure, les Velocimacros doivent être définies avant d'être utilisées dans un gabarit. Ce qui signifie que les déclarations #macro() doivent précéder l'usage des Velocimacros.

Il est important de s'en souvenir si l'on essaye d'interpréter avec #parse() un gabarit qui contient des directives #macro() définies inline. Puisque le #parse() a lieu au moment de l'exécution, et que l'interpréteur décide à ce moment si quelque chose qui ressemble syntaxiquement à une VM dans le gabarit en est vraiment une au moment de l'interprétation, #parse()-r un ensemble de déclarations de VM ne fonctionnera pas comme on pourrait le prévoir. Pour contourner ce comportement, il suffit d'utiliser velocimacro.library pour que Velocity charge vos VM's au démarrage.

Qu'est-ce que le Velocimacro Autoreloading?

Il existe une propriété, conçue pour le développement, pas pour la production:

velocimacro.library.autoreload

dont la valeur par défaut est false. Lorsque cette propriété est positionnée à true, en même temps que

<type>.resource.loader.cache = false

(où <type> est le nom du chargeur de ressources que vous utilisez, par exemple 'file'), le moteur Velocity va automatiquement prendre en compte les changements apportés à vos fichiers de bibliothèques Velocimacro, au fur et à mesure que vous les modifiez, de sorte que vous n'ayez pas à arrêter le moteur de servlet (ou l'application) ou avoir recours à tout autre subterfuge de ce genre pour recharger vos Velocimacros.

Voici à quoi peut ressembler un ensemble simple de propriétés.

    file.resource.loader.path = templates
    file.resource.loader.cache = false
    velocimacro.library.autoreload = true
    

Ne gardez pas cette configuration en production.

Concaténation de chaînes

Une question habituelle parmi les développeurs est: Comment concaténer des chaînes de caractères? Y a-t'il quelque chose de semblable à l'opérateur '+' en Java?.

Pour concaténer des références en VTL, il suffit de les 'mettre ensemble'. Le contexte dans lequel vous voulez les assembler ainsi a une certaine importance, il faut donc illustrer notre propos par quelques exemples.

Dans le flux habituel d'un gabarit (lorsqu'on mélange les références avec du contenu ordinaire):

       #set( $size = "Big" )
       #set( $name = "Ben" )

      Cette horloge sonne comme $size$name.
   

produira 'Cette horloge sonne comme BigBen'. Pour prendre des exemples plus intéressants, lorsqu'on veut concaténer des chaînes pour les passer à une méthode ou les assigner à une nouvelle référence, il suffit d'écrire:

      #set( $size = "Big" )
      #set( $name = "Ben" )

      #set($clock = "$size$name" )

      Cette horloge sonne comme $clock.
    

Ce qui produit le même résultat. Dernier exemple, lorsqu'on veut mélanger des chaînes "statiques" avec des références, il peut être nécessaire d'utiliser les "références formelles" rencontrées plus haut:

      #set( $size = "Big" )
      #set( $name = "Ben" )

      #set($clock = "${size}Tall$name" )

      Cette horloge sonne comme $clock.
    

Et maintenant le résultat produit est 'Cette horloge sonne comme BigTallBen'. La notation formelle est requise pour que l'interpréteur comprenne que l'on souhaite utiliser '$size' et non '$sizeTall', ce qui serait le cas si les accolades n'étaient pas présentes.

Donnez votre avis

Si vous rencontrez des erreurs dans le manuel (autres que des erreurs de traduction), ou si vous voulez faire part de votre avis sur le Guide de l'utilisateur Velocity, envoyez un mail à la Velocity user list. Merci!