Potion 1.0.0
Potion est un moteur de templates (template engine) implémenté en JavaScript pour ForumActif ou pour tout autre projet.
- Ce plugin est un outil. Il n'est pas vraiment exploitable en l'état et demande de bonnes notions en JavaScript pour être utilisé à son plein potentiel.
Installer le script
Sur forum
Le plugin est compilé en un seul fichier et doit être installé en script externe. Ouvrez votre template overall_footer_end
(fin du bas de page) et juste avant la fermeture de la balise </body>
, insérez le code suivant :
<!-- Importation de Potion.js -->
<script src="https://cdn.jsdelivr.net/npm/@poumon/potion@1/potion.min.js"></script>
<!-- Importation de Potion.js Diète -->
<script src="https://cdn.jsdelivr.net/npm/@poumon/potion-diet@1/potion.min.js"></script>
Via npm
Pour la toute première fois 🥳 un de mes plugins peut être utilisé tel quel dans n'importe quel projet et n'est pas dépendant de l'environnement ForumActif pour fonctionner.
Pour utiliser Potion dans vos projets, vous pouvez utiliser la commande npm install @poumon/potion
ou npm install @poumon/potion-diet
et l'importer comme module :
import potion from "@poumon/potion"
// OU
import potion from "@poumon/potion-diet"
Utilisation du plugin
Potion et Potion Diet
Il existe deux versions de Potion : la principale et la plus puissante (tout inclus) et "Diète", une version simplifiée et plus compacte. La fonction principale des deux versions est identique et permet d'imiter un moteur de template en JavaScript :
const template = "Bonjour, je m'appelle [user.name] et j'ai [user.cats.length] chats."
const data = {
user: {
name: "Poumon",
cats: ["Boudin", "Babushka"],
}
};
const output = potion(template, data);
// "Bonjour, je m'appelle Poumon et j'ai 2 chats."
Il est également possible, avec les fonctionnalités principales, de travailler avec des templates imbriqués, de cette façon :
const data = { catItems: ['Croquettes', 'Herbe (à chat)', 'Jouet ressort'] };
let ul = "<ul>[list]</ul>",
li = "<li>[item]</li>",
body = "";
for (let i = 0; i < 2; i++){
body += potion(li, { contents: data.catItems[i] });
}
const output = potion(ul, { list: body });
// "<ul><li><a>Croquettes</a></li><li><a>Herbe (à chat)</a></li><li><a>Jouet ressort</a></li></ul>"
Ou d'utiliser des tableaux de cette façon :
potion("Hello [0] and [1]!", ["Boudin", "Babushka"]);
// retourne "Hello Boudin and Babushka!"
potion("Hello [cats.0] and [cats.1]!", { cats: ["Boudin", "Babushka"] });
// retourne "Hello Boudin and Babushka!"
En soi, Potion permet de remplacer ce qu'on appelle des "jetons" (tokens) dans une chaîne de texte par des données. L'appel de la fonction potion(template, data)
retourne la valeur du template une chaîne de charactères une fois les jetons remplacés.
Fonctionnalités avancées
Prenez note que la suite des fonctionnalités ne concernent pas Potion Diète.
Options
Potion est pensé en prenant en compte les restrictions d'écriture rencontrées dans les templates de ForumActif. Il est toutefois possible de changer les délimiteurs en accolades { }
au besoin.
// potion(template, data, options)
potion(template, data, {
start: "{",
end: "}"
});
- start | Par défaut,
[
- end | Par défaut,
]
Si vous décidez de modifier ces délimiteurs, assurez-vous de ne pas utiliser d'accolades dans un script à l'intérieur d'un template. ForumActif n'aime pas qu'on touche à ses accolades et risquerait de les interpréter comme les siennes.
Micro-template dans le DOM
L'élément HTML <template>
permet de stocker du HTML qui ne sera pas affiché lors du chargement initial de la page, mais qui peut être instancié et affiché par la suite grâce à du JavaScript.
Potion est capable de récupérer ces éléments <template>
, à condition qu'ils soient identifiés pour lui. Il existe deux façons de faire :
- En créant un
<template type="template/potion">
anonyme. -
<template type="template/potion" data-name="babushka">
nommé, grâce à l'attributdata-name
.
Un avantage non négligeable d'utiliser <template>
est de conserver le HTML dans un fichier HTML et la logique des données dans un fichier JavaScript à part, ce qui permet de ne pas travailler le HTML en chaîne de caractères (sans tabulation, sans structure, sans beauté) dans une variable JavaScript.
De plus, au chargement du Document, Potion place immédiatement tous les <template>
qu'il reconnaît dans son cache.
<!-- Le plus collant, Boudin -->
<template type="template/potion">
<div class="[cat.boudin.bgcolor]">
<img src="[cat.boudin.img]" alt="" />
</div>
</template>
<!-- La plus patate, Babushka -->
<template type="template/potion" data-name="babushka">
<div class="[cat.babushka.bgcolor]">
<img src="[cat.babushka.img]" alt="" />
</div>
</template>
La seule différence entre un <template>
anonyme et un nommé, c'est la façon de l'appeler.
Les templates anonymes sont enregistrés selon l'ordre dans lequel ils sont rencontrés dans le DOM, en prenant en compte tous les <template type="template/potion">
selon la convention suivante [data-name] OU `potion-${i}`;
const data = {
cat: {
boudin: {/* ... */},
babushka: {/* ... */},
}
};
// Le plus collant, Boudin
const boudin = potion('potion-0', data);
// La plus patate, Babushka
const babushka = potion('babushka', data);
Puisque Potion met en cache le contenu des balises <template>
qu'il reconnaît, vous pouvez les récupérer en indiquant leur nom comme premier argument de la fonction potion(template, data)
.
Syntaxe
Utiliser des conditions de rendu
Potion supporte l'utilisation de conditions booléennes permettant l'affichage d'un rendu en fonction d'une valeur true
ou false
dans les données.
const template = "[isLoggedIn][secret][/isLoggedIn]";
const data = { isLoggedIn: user.loggedIn(), secret: "Poumon aime beaucoup ses chats." };
potion(template, data);
D'après l'exemple ci-dessus, la valeur de secret
ne sera révélée que si l'utilisateur est connecté.
Parcourir des tableaux
Comme précédemment présenté, Potion peut traitées des données en tableaux.
<template type="template/potion" data-name="cats">
Les plus beaux chats du monde :
[cats]
[name] est [color].
[/cats]
</template>
potion("cats", {
cats: [
{ name: "Boudin", color: "noir" },
{ name: "Babushka", color: "écaille de tortue" }
]
});
Comme le template ci-dessus est nommé cats
, nous pouvons le récupérer en passant simplement son nom comme premier argument de la fonction.
Utiliser des fonctions
Potion est capable de détecter une fonction et de l'exécuter. À l'intérieur de celle-ci, le mot-clef this
fait référence à l'objet passé en données.
potion("boudin", {
name: "Boudin",
famousQuote: function() {
return `REGARDE-MOOOEEEEEW —${this.name}`
}
});
Pour des raisons de sécurité, les fonctions ne sont malheureusement pas autorisées à l'intérieur d'un <template>
. Par exemple [name.toUpperCase()]
ne sera pas exécuté. Il est préférable de transformer vos données directement depuis l'objet data.
Parcourir des objets
Il est possible de traverser des objets. Lorsque potion détecte un objet, il mettra à disposition deux nouveaux "jetons" pour accéder aux propriétés de l'objet.
<template type="template/potion" data-name="cats">
Les plus beaux chats du monde :
[cats]
[_key] est [_value].
[/cats]
</template>
potion("cats", {
cats: {
boudin: "noir"
babushka: "écaille de tortue"
}
});
Interprétation d'un template
Il est également possible de placer un <template>
quelque part dans le DOM et de le transformer en véritable HTML une fois les données reçues. Tous les <template>
doivent être placés avant l'import du script.
Rendering
La fonction potion.render(template, data, options)
transformera la balise <template>
en une <div>
(ou tout autre balise spécifiée dans les options durant l'appel).
Contrairement à potion()
, potion.render()
ne fonctionne qu'en ciblant un template par son nom, puisque celui-ci sera remplacé dynamiquement.
const div = potion.render('cats', data, {
tag: 'section',
class: 'visible'
});
- tag | Par défaut, le
<template>
sera transformé en<div>
et conservera tous les attributs dont il disposait (incluant son[data-name]
). Il est toutefois possible de le transformer en autre chose en spécifiant la balise d'un élément HTML valide. - class | Comme tous les attributs sont transférés à l'élément transformé, cette option permet d'ajouter des classes supplémentaires une fois le
<template>
transformé.
{1.2.0} Syncing
La fonction potion.sync(template, data, options)
transformera la balise <template>
en une <div>
(ou tout autre balise spécifiée dans les options durant l'appel).
Contrairement à potion()
, potion.sync()
ne fonctionne qu'en ciblant un template par son nom, puisque celui-ci sera remplacé dynamiquement.
- tag | Par défaut, le
<template>
sera transformé en<div>
et conservera tous les attributs dont il disposait (incluant son[data-name]
). Il est toutefois possible de le transformer en autre chose en spécifiant la balise d'un élément HTML valide. - class | Comme tous les attributs sont transférés à l'élément transformé, cette option permet d'ajouter des classes supplémentaires une fois le
<template>
transformé.
const data = {
cats: {
boudin: "noir"
babushka: "écaille de tortue"
}
};
const syncedData = potion.sync('colors', data, {
tag: 'section',
class: 'visible'
});
En revanche, potion.sync()
retourne directement le clonde de l'objet passé en data sous la forme d'un proxy. Dans l'exemple ci-dessus, toutes les modifications effectuées aux valeurs de syncedData
seront reflétées directement dans le DOM.
syncedData.cats.boudin = "blanc"
serait faux (parce que boudin est noir)fonctionnerait de la même façon que si vous étiez allé sélectionner le div dans lequel le texte est affiché, et que vous aviez utilisédiv.textContent = 'blanc'
(même si c'est faux).
{1.2.0} Gestion des événements
En plus des jetons, il est maintenant possible d'ajouter des événements directement dans les <template>
.
Les événements sont ajoutés comme attribut d'un élément et sont précédés d'un @
comme dans <div @click="">
. Il s'agit d'un "event listener". Vous trouverez une liste complète des événements du DOM
en suivant ce lien
.
Il existe deux façons d'utiliser les événements dans un <template>
.
Événements intégrés
Cette façon de faire permet de modifier la valeur d'un jeton sans devoir passer par l'exécution d'une fonction. Seules les méthodes primitives relatives au type de jeton sont acceptées.
@click="catsNum++"
et @click="cats.push('Babushka')"
Événements déclarés
Les événements déclarés permettent d'appeler une fonction présente dans l'objet data
passé en argument lors de l'initialisation du <template>
. Tous les arguments de type "variable" doivent aussi existés dans l'objet data
.
@click="petCat('Boudin', cats)
{1.2.0} Modificateurs
Les modificateurs sont des suffixes appliqués aux directives d'un événement qui permettent de changer le comportement d’un événement sans écrire de code supplémentaire.
Exemples concrets :
@click.stop
→ Empêche la propagation de l’événement commeevent.stopPropagation()
.@submit.prevent
→ Empêche le rechargement de la page lors de l’envoi d’un formulaire commeevent.preventDefault()
.@keyup.enter
→ Déclenche l’action uniquement quand on appuie sur "Entrée".
Événement
.stop
.prevent
Clavier
Les événements claviers supportés sont @keyup
(appui d'une touche), @keydown
(au relâchement d'une touche appuyée). Potion supporte ces touches comme modificateurs.
.enter
.tab
.delete
(fonctionne à la fois pour "Delete" et "Backspace").esc
.space
.up
.down
.left
.right
Souris
.left
.right
.middle
Ce qu'il me reste à faire
- Dans un monde idéal, j'aimerais m'inspirer de React et permettre une façon de transformer les
<template>
en "DOM réactif" de sorte à ce que les données restent connectées à leur noeuds HTML (et donc, modifiable en temps réel si l'objet data est modifié).Dans un premier temps, il me faudra proposer une façon de parser unet le transformer en shadowdom ouvert, peut-être grâce à une fonction déconstruite<template>
potion.templates('name', data).render()
. - Optimiser la fonction
.sync()
. - Vérifier le support async/await des fonctions dans les données.