[ad_1]

Utilisez des expressions régulières, des chaînes de modèle et des fonctions dynamiques pour gérer les chaînes de modèle HTML de manière simple et rapide

Ingredients: Function class, Regular Expressions, Template Literals, and some curiosityCooking time: 10 minutes

Il existe plusieurs moteurs de modèles JavaScript disponibles sur le Web. Allant de ceux qui font des performances leur point fort, à ceux qui mettent l’accent sur la syntaxe et la compréhensibilité du code.

Au-delà de ces aspects, il existe de très bons produits qui sont définitivement le choix idéal dans le cas de projets qui utilisent intensivement des modèles de texte.

Bien sûr, ce ne sera pas quelque chose d’écrit en trois lignes pour concurrencer des systèmes beaucoup plus puissants, mais il peut être utile et intéressant d’envisager d’autres mécanismes de production de texte à partir d’un modèle.

De plus, quelque chose de cuisiné sur le menu pourrait avoir un goût intéressant.

Normalement, un moteur de modèle fournit sa propre syntaxe, qui utilise pour appeler des opérations spécifiques, telles qu’un for boucle qui nous permet de répéter du texte en faisant varier les valeurs des variables impliquées dans l’itération. Ayant sa propre syntaxe, il est normal que chaque moteur doive implémenter un parseur, et d’autres fonctionnalités, pour pouvoir interpréter correctement les instructions et par conséquent produire le texte que nous attendons en sortie.

Cependant, le titre de l’article parle de trois lignes de code. Alors, comment est-il possible d’implémenter un parseur et toutes les fonctionnalités nécessaires à un moteur en seulement trois lignes de code ? Eh bien… ce n’est pas possible, mais ce n’est pas un problème.

En fait, nous avons déjà un assez bon interpréteur disponible et c’est le même runtime JavaScript où notre code s’exécute. Si nous ajoutons les Template Strings de ES6 et une pincée d’expressions régulières, nous avons ici tous les ingrédients pour notre moteur compact.

Pour plus de détails:

Disons que nous avons une structure de données et que nous devons produire du code HTML en y injectant des valeurs et en répétant également des blocs de code. En clair, chaque bloc de code répété devra injecter des valeurs différentes lues à partir des données sources.

Structure de données:

Ce que nous aimerions réaliser :

De toute évidence, la solution n’est pas de créer une fonction JavaScript triviale, mais d’écrire du code qui concatène des chaînes en lisant les données transmises. Bien sûr, ce type de solution produirait un bon résultat, mais elle n’est pas polyvalente du tout et elle est loin du concept d’un moteur de template.

Nous voulons avoir notre modèle dans une chaîne JavaScript contenant du texte, dans ce cas, du code HTML, ainsi que du code JavaScript qui peut effectuer des itérations sur les données et d’autres opérations typiques du langage.

Oh, j’oubliais, nous ne voulons même pas écrire un parseur ! (au moins pas aujourd’hui).

Essayons de décomposer les parties impliquées en tenant compte des données de départ et du résultat que nous voulons obtenir.

Nous avons la première donnée que nous souhaitons produire en sortie : le titre de la série télévisée. Dans ce cas, il s’agit d’un script « ponctuel » qui ne demande qu’à injecter la valeur du title propriété à l’intérieur d’un <div> étiquette.

Notre modèle pourrait commencer comme ceci :

<div class="title">{= data.title =}</div>

Nous remarquons immédiatement l’utilisation d’une syntaxe particulière dans la balise et, à son tour, dans celle-ci, une expression typiquement JavaScript pour accéder à un title propriété d’un objet nommé data.

Analysons immédiatement ces premiers éléments :

La syntaxe {= ... =} nous permet d’introduire une instruction à l’intérieur du modèle afin de résoudre une valeur en la lisant contextuellement à partir des données transmises à l’appel de la génération suivante.

L’identifiant data fait référence à la racine de l’objet passé, qui peut être un objet JavaScript, un tableau ou toute autre valeur. Dans notre cas, c’est un objet qui contient une première propriété titlequi est celui que nous souhaitons résoudre en tant que valeur à insérer dans la sortie.

Le choix d’utiliser {= et =} comme délimiteurs est entièrement arbitraire. Vous pouvez utiliser n’importe quelle composition de caractères tant qu’elle n’entre pas en conflit avec d’autres parties du modèle. Autrement dit, il n’y a aucune ambiguïté avec d’autres éléments possibles du modèle qui devraient se retrouver dans la sortie sans être transformés.
Par exemple, si nous utilisions trivialement les caractères < et > il serait plus difficile de distinguer un espace réservé d’une valeur qu’une balise HTML de sortie.

À ce stade, nous devons transformer les éléments enfants de notre objet « série télévisée », en faisant défiler les noms de l’épisode et en les insérant dans la sortie à l’intérieur d’un div étiquette.

Mais avant de décider que les éléments enfants doivent résider dans un autre div balise représentant la liste des épisodes. Par conséquent, nous devons ajouter la balise d’ouverture suivante au modèle :

<div class="episodes-list">

Cette partie sera imprimée dans la sortie sans aucune transformation.

Différentes conditions pour la partie suivante qui devront plutôt différer de ce qui est exprimé dans le modèle. Il va falloir déclarer une variable contenant le numéro de l’épisode (qui sera calculé dynamiquement en incrémentant la variable dans la boucle) et, pour faire défiler les épisodes, il va falloir du code JavaScript en boucle for..of.

Dans notre modèle, nous allons ajouter la variable et la partie initiale du for..of construction:

let episodeNumber = 1;
for (let episodeName of data.episodes) {

Encore une fois, nous nous référons à l’objet principal data dont nous parcourons le tableau contenu dans le episodes propriété.

À l’intérieur de la boucle, nous devrions imprimer le code HTML avec le numéro de l’épisode et la valeur relative de episodeNamecomme le nième élément de l’itération.

<div class="episode-name"><span>{= episodeNumber =}</span><span>{= episodeName =}</span></div>

Nous incrémentons notre variable et concluons en fermant le for..of construction:

episodeNumber++;
}

Enfin, nous fermons également le div balise de la liste des chapitres, ouverte avant la for..of boucle.

</div>

En mettant tout cela ensemble, imaginons que nous avons construit le modèle suivant :

À première vue, cela peut sembler correct, mais de cette façon, notre « moteur » n’est pas encore capable de distinguer le texte de sortie du code JavaScript, nous devons donc prendre un peu de recul pour modifier notre modèle en insérant un délimiteur (un ou plus de caractères) qui indique la transition du texte statique aux instructions JavaScript et vice versa.

Le critère pour choisir un tel délimiteur peut varier en fonction de plusieurs facteurs, comme le type de texte de sortie (HTML ou autre) ou par exemple la position où se trouve notre texte (dans un fichier HTML externe, à l’intérieur d’une chaîne JavaScript multi-lignes, etc…).

Pour ce cas simple et spécifique, j’ai choisi d’utiliser la séquence de caractères ## parce qu’il met visuellement en évidence la transition du code HTML au code JavaScript et parce qu’il pourrait difficilement entrer en conflit avec quoi que ce soit d’autre.
À ce stade, notre modèle devrait ressembler à ceci :

Ou de cette façon si on le trouve plus lisible (les deux blocs de code sont équivalents).

Rien de plus. Au niveau du modèle, rien d’autre n’est nécessaire pour que notre « moteur » fasse son travail.

De cette façon, il pourra séparer le texte des instructions avec très peu d’opérations et le transformer en texte final.

Jusqu’à présent, nous n’avons vu que la partie liée à la technique d’écriture de template, mais ce qui était promis au début était la recette pour créer un moteur de template en seulement trois lignes de code, il est donc temps de passer à la partie où nous « cuisinons » ” notre moteur.

Les étapes requises sont peu nombreuses et concises :

  1. Post-traitement de la chaîne de modèle :
    UN. ajoute au début et à la fin du texte du modèle le même délimiteur utilisé pour séparer le code statique des instructions JavaScript.
    B. remplace, par deux expressions régulières différentes, les délimiteurs par du texte spécifique (que nous verrons bientôt).
    C. remplace, toujours par expression régulière, les espaces réservés des valeurs {= exp =} avec les mêmes expressions sous la forme ${exp}.
    Par exemple: {= data.title =} devient ${data.title}.
    . supprime tous les délimiteurs orphelins au début et à la fin de la chaîne de modèle.
  2. Produit dynamiquement une Function qui se charge de transformer le texte.
  3. Appelle la fonction en lui passant l’objet utilisé comme argument et produit le texte final.

Voyons tout à l’intérieur d’une fonction simple qui accepte deux arguments : le premier est le texte du modèle (texte statique et éventuelles instructions JavaScript), et le second est tout objet de données à partir duquel les valeurs seront lues.

D’accord, d’accord. J’ai dit trois lignes et ici nous en avons au moins 20… mais si nous supprimons tous les commentaires inutiles et les retours chariot, ce qui reste est

Prêt sur la table !

Certains d’entre vous ont peut-être remarqué que j’aurais pu écrire « …en une seule ligne de code » (ce qui est possible !) mais peut-être, vu l’illisibilité du code produit, cela aurait été du clickbait.
De toute façon 😉

Voyons ce qui se passe en détail en une seule étape, en voyant même comment notre texte de modèle se transforme de temps en temps.

On part de cette condition

Nous ajoutons des délimiteurs au début et à la fin

On utilise l’expression régulière ##\s*< pour capturer le motif ## + (space or empty) + < et de les remplacer par (new line) + out.push(`<et l’expression régulière >\s*## pour les patrons > + (space or empty) + ## en les remplaçant par >`); + (new line). Ce que nous obtiendrons est :

À ce stade, il y a la dernière étape de post-traitement qui consiste à remplacer nos espaces réservés (le cas échéant) par le format d’espace réservé géré par les littéraux du modèle JavaScript. Nous allons remplacer les caractères {= avec ${ et =} avec }afin d’obtenir:

Avec cette dernière substitution, nous avons transformé notre modèle en code JavaScript correct. Il ne nous reste plus qu’à l’insérer dans une fonction dynamique capable de l’exécuter et, enfin, appeler la fonction pour produire le résultat final.

La classe Function du langage JavaScript sera utilisée pour générer la fonction, grâce à laquelle il est possible de définir comme chaîne, à la fois les paramètres qu’elle acceptera, et le corps de la fonction elle-même.

Pour plus de détails:
Fonction MDN

Voici notre code de référence :

J’ai inséré deux lignes de commentaire délimitant afin de mieux le comparer avec la fonction finale produite

Nous créons une fonction dynamique via la classe Function, à laquelle nous transmettons une première chaîne qui représente le nom d’un paramètre, et un second (dernier) argument qui représente le corps de la fonction. Nous pouvons voir l’utilisation de la chaîne multiligne, pour la définition du corps, qui nous permet d’insérer un espace réservé qui appelle le modèle transformé précédemment.
Ce que le runtime va générer en mémoire ressemblera à ceci :

La composeFunc constante contiendra la référence à la fonction, que vous pouvez appeler comme n’importe quelle autre fonction explicitement déclarée.
Par conséquent:

return composeFunc(data);

renverra le texte final que nous avons cherché à obtenir :

dont le rendu avec des styles simples ressemblera à ceci :

.title {
font-weight: 700;
font-size: 1.2em;
margin-bottom: 10px
}
.episode-name > span:first-child {
display: inline-block;
text-align: right;
font-weight: 500;
margin-right: 20px;
min-width: 2ch;
}

Nous avons vu comment utiliser les expressions régulières, les chaînes de modèles et les fonctions dynamiques pour gérer les chaînes de modèles HTML de manière simple et rapide.
Il est juste de préciser que ce que nous avons réalisé, bien que polyvalent, est loin d’être considéré comme un véritable moteur de template. Cela peut être une bonne idée de départ mais il faudrait introduire de nombreuses autres fonctionnalités et validations qui rendraient le système plus pratique et plus sûr.

Ce que nous avons fait est plutôt un exercice de style qui nous permet de « nous salir les mains » et de voir de près comment certaines fonctionnalités fonctionnent lorsqu’elles sont combinées.

Comme pour tout nouveau plat, la première réalisation apporte souvent avec elle des défauts et des approximations. Mais la beauté de l’expérimentation est d’apprendre de nouvelles choses, à la fois lorsque les ingrédients se combinent bien et lorsqu’ils ne le font pas. Cela n’enlève rien au fait que les recettes peuvent être améliorées et, parfois, les défauts peuvent être transformés en points forts. Um… J’ai déjà quelque chose en tête, je pense que nous y reviendrons à l’avenir.

Restez curieux, restez créatif !

Un grand merci à mon amie Chiara Bernardini pour son soutien (L)

[ad_2]

Télécharger ici

Leave a Reply

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Instagram

Ce message d’erreur n’est visible que pour les administrateurs de WordPress

Erreur. Aucun flux trouvé.

Veuillez aller sur la page de réglages d‘Instagram Feed pour connecter votre compte.