Templates, données, état (Node.js+Express)
Dans ce TD nous introduisons le moteur de templates Twig, et nous
étudions quelques techniques de maintien d’état. Vous pouvez continuer
à travailler dans le même espace de travail Cloud9. Alternativement,
vous pouvez créer un nouvel espace de travail en clonant
https://github.com/defeo/aws-project.git ; n’oubliez pas de lancer à
nouveau la commande npm install
dans ce cas.
Les références pour ce TD sont
- Le manuel de JavaScript,
- La référence de Node.js,
- Les aides de NodeJitsu,
- Le guide de Express,
- La référence de Express,
- Ce tutoriel sur Twig,
- La documentation de Twig.
Contenu statique et templates
Dans le TD précédent, nous avons utilisé deux techniques très différentes pour créer nos pages :
- Pour les URL dynamiques, comme
/query_string
, etc., nous avons défini des gestionnaires dans un fichier JavaScript. La sortie HTML a été obtenue à coups de concaténations de chaînes de caractères. - Pour le formulaire statique, nous avons créé un simple fichier HTML.
Ce sont deux manières de coder opposées, et il serait dommage d’être limités à cela et de ne pas pouvoir les mélanger. Dans cette section nous allons apprendre comment Express permet de lire et servir des fichiers statiques, puis comment le moteur de templates Twig permet de réaliser une transition douce entre les pages statiques et les pages dynamiques.
Servir des fichiers
-
Créez un fichier
form.html
, contenant un formulaire comme ceciChoisissez une couleur
rouge
jauneTâchez de créer une page HTML complète et valide : avec
<!Doctype>
, entêtes,<body>
, etc. Ne vous souciez pas des attributsmethod
etaction
, pour l’instant. -
Créez un fichier
app.js
, et initialisez-le avec le squelette habituel de Express :var express = require('express'); var bodyP = require('body-parser'); var cookieP = require('cookie-parser'); var app = express(); app .use(bodyP.urlencoded({ extended: false })) .use(cookieP()); // Vos gestionnaires ici app.listen(process.env.PORT);
(voir aussi le fichier
exemple.js
)). -
Créez un dossier
static
et déplacez-y le fichierform.html
. Ajoutez à la configuration deapp.js
la ligneapp.use('/s', express.static('static'));
Ceci permet à Node.js de servir les fichiers contenus dans le dossier
static
à l’URL/s/...
. Exécutezapp.js
et vérifiez que le formulaire est bien servi à l’url/s/form.html
-
Nous allons aussi servir le même document à une autre URL. Créez un gestionnaire pour l’URL
/signin
, et utilisez la méthoderes.sendFile()
pour servir le formulaire (la méthode prend un paramètre : le chemin du fichier à servir).Note : la methode
res.sendFile()
demande le chemin absolu vers le fichier à servir. La façon la plus portable de donner ce chemin est d’utiliser la variable globale__dirname
prédéfinie par Node.js, comme cecires.sendFile(__dirname + '/static/nom_du_fichier.html');
-
Créez une deuxième page html (complète et valide) contenant simplement le texte « Bonjour ! ». Servez cette page à l’url
/hello
par la méthodesendFile
. Modifiez le formulaire pour qu’il dirige vers cette page.
Templates Twig
Un template est un modèle d’un document (usuellement un document HTML, mais ce n’est pas obligatoire) avec du marquage spécial qui indique les parties dynamiques du document.
Voici un template très simple :
<p>Hello {{ nom }} !</p>
Le marquage spécial {{ nom }}
indique qu’il faudra remplacer à cet
endroit la valeur de la variable nom
.
Chaque langage de templating définit sa propre syntaxe, plus ou moins riche. Node dispose d’une pléthore de moteurs de templating ; pour s’en faire une idée, il suffit de regarder la liste disponible sur le serveur de paquets de Node.
Pour rester compatibles avec la partie du cours faite en PHP, ce TD
va utiliser langage de templating Twig, voici un
tour rapide de sa syntaxe. Les
seuls composants dont nous allons nous servir dans ce TD sont le
remplacement de variables, montré dans l’exemple précédent, et les
blocks if
et for
.
Sachez, cependant, que le module Twig pour Node.js est encore en phase expérimentale. Vous êtes encouragés à explorer d’autres langages de templating populaires : le plus proche de Twig est Nunjucks, mais peut-être trouverez-vous votre bonheur avec Hogan (Mustache), Pug, …
Important : avant d’utiliser Twig (ou tout autre moteur de templating), il faut le charger avec
var twig = require('twig');
et dire à Express où se trouvent les fichiers de templates. Par exemple, la configuration
app.set('views', 'templates');
dit à Express de chercher les fichiers de templates dans le dossier
templates
. Attention : seuls les fichiers avec extension .twig
seront passés au moteur Twig**; pour configuer Express de façon plus
flexible, lisez la doc de
app.engine
.
Ensuite, en supposant que le template montré plus haut soit dans le
fichier templates/hello.twig
, il serait exécuté ainsi
res.render('hello.twig', { 'nom' : 'Toto' });
ce qui produirait la sortie
<p>Hello Toto !</p>
Important : Pour se protéger des attaques XSS, il est aussi fortement recommandé d’activer l’échappement automatique des variables avec
app.set("twig options", { autoescape: true });
-
Étudiez à nouveau le fichier
example.js
et comprenez comment il utilise les templates Twig. -
Éditez le gestionnaire et le template à l’url
/hello
pour que le texte affiché soit « Bonjour, … ! », où le nom rentré dans le formulaire apparaît à la place des points de suspension.
Voici maintenant un exemple de template utilisant la boucle for
.
<ul>
{% for en, fr in nombres %}
<li>{{ en }} signifie {{ fr }}</li>
{% endfor %}
</ul>
Cet exemple, exécuté par l’appel
res.render('boucle_for.twig', { 'nombres' : {
'One' : 'Un',
'Two' : 'Deux',
'Three' : 'Trois'
} });
produit le code
<ul>
<li>One signifie Un</li>
<li>Two signifie Deux</li>
<li>Three signifie Trois</li>
</ul>
Attention : il n’y a pas de foreach
dans Twig. Pour plus
d’exemples d’utilisation de la boucle for
, voir
http://twig.sensiolabs.org/doc/tags/for.html.
-
Insérez dans votre code JavaScript ces deux tableaux :
['cerise', 'fraise', 'sang'] ['soleil', 'citron', 'banane']
Dans la page
/hello
, à l’aide d’un bloc{% if %}
et d’un bloc{% for %}
, affichez l’un ou l’autre, selon la couleur choisie dans le formulaire. -
Ajoutez une balise
<style>
dans l’entête du template. Servez-vous-en pour colorer en rouge ou en jaune le texte de la liste, selon la couleur choisie dans le formulaire.
Persistance
Nous étudions maintenant les différentes techniques de persistance des données. Nous allons faire persister le nom saisi par l’utilisateur à travers plusieurs pages.
Persistance par le query string
Nous commençons par faire persister les données dans le query string.
-
Ajoutez un lien (balise
<a>
) dans la page/hello
vers une nouvelle page/bye
. -
Toujours à l’aide du moteur de templates, servez une page à l’url
/bye
, contenant le texte « Au revoir, … », où les points sont remplacés par le nom saisi par l’utilisateur. Vous pouvez recevoir cette donnée par le query string, par exemple, en répondant aux urls de la forme/bye?nom=toto
.
Persistance par champs cachés
Nous passons maintenant aux techniques de persistance par la méthode POST. Cette technique peut être melangée avec la précédente.
-
Modifiez
form.html
pour qu’il envoie ses données par la méthode POST. -
Créez un gestionnaire pour les requêtes de type POST à l’url
/hello
. Affichez « Bonjour, … », comme vous l’avez fait plus tôt. -
Ajoutez dans
/hello
un formulaire contenant un champ de texte prérempli avec le nom de l’utilisateur, et un bouton de soumission. Le formulaire pointera vers l’url/bye
. -
Créez un gestionnaire pour les requêtes de type POST à l’url
/bye
. Affichez « Au revoir, … » comme vous l’avez fait plus tôt. -
Modifiez le formulaire de l’url
/hello
en transformant le champ de texte en champ de typehidden
.
Persistance par l’URL
Dernière technique légère de persistance : garder les données directement dans l’url. Cela est rendu possible par le routeur de Express. Nous changeons légèrement d’exercice : nous réalisons un compteur de visites.
-
Créez un gestionnaire
app.get('/:nom/compteur', ...);
À l’aide d’un template, affichez le texte « …, ceci est votre première visite », où vous remplacerez à la place des points le nom passé par l’url.
-
Créez un gestionnaire pour les urls de la forme
/:nom/compteur/:cnt
. Le composant:cnt
de l’url va être un entier qui compte le nombre de visites. Affichez le texte « …, vous avez visité cette page … fois », où vous remplacerez:nom
et:cnt
à la place des points.Note : Vous pouvez restreindre
:cnt
à des valeurs exclusivement numériques avec une expression régulière. -
Ajoutez un lien dans
/:nom/compteur
vers/:nom/compteur/1
. Ajoutez un lien dans/:nom/compteur/:cnt
vers la même page, avec le compteur augmenté.
Si vous avez correctement exécuté l’exercice, vous avez un compteur qui augmente d’une unité à chaque clic sur le lien. Vous pouvez, bien sûr, appliquer la même technique au query string ou aux formulaires cachés. Essayez pour vous en convaincre.
-
Donnez un prix à la millionième visite.
-
Sans cliquer un million de fois, gagnez le prix.
Cookies et Storage API (optionnel)
Nous arrivons aux deux dernières techniques de persistance gérée par le client. Les cookies sont une extension du protocole HTTP, ils sont manipulés par le serveur et stockés par le client. La storage API est une partie de la spécification du DOM, elle est totalement manipulée par le client via JavaScript.
Cookies
Les cookies sont des couples clef-valeur stockés par le client. Un gestionnaire peut demander à stocker un cookie avec le code suivant
res.cookie('user', 'toto');
Une fois stockés, les cookies sont envoyés par le client avec toute
requête pour le même domaine (peu importe le gestionnaire). Ils sont
lus dans l’objet req.cookies
par le middleware cookie-parser
,
comme déjà vu au TD précédent.
-
Créez une page
/compteur-cookie
qui compte le nombre de visites à elle même. L’information sera stockée dans un cookie. Si le cookie n’est pas initialisé (par ex., à la première visite), celui-ci sera initialisé à 1. S’il est initialisé, sa valeur sera augmentée de 1 et il sera envoyé à nouveau au client. Pour voir le nombre de visites augmenter, il suffira de recharger la page. -
Offrez un prix à la millionième visite.
-
Gagnez le prix (suggestion : il va probablement falloir installer une extension pour votre browser).
Storage API
La storage API se gère entièrement par JavaScript, vous pouvez donc utiliser une page html statique, sans passer par Node.
Il s’agit d’un simple tableau, localStorage
, qui persiste aux
rechargements de la page et à la fermeture du browser.
-
Créez une page qui compte son nombre de visite à l’aide de l’API
localStorage
. Rechargez la page et vérifiez que cela fonctionne. -
Offrez un prix à la millionième visite.
-
Gagnez le prix (la console JavaScript devrait vous suffire).