Le dev simple, efficace et élégant.

Les super pouvoirs des langages de template - partie 3

Dernière édition :

Table des matières

Cet article est le dernier d'une série consacrée aux langages de templating côté serveur.

L'article précédent montrait comment injecter des données dans les pages HTML, et comment gérer efficacement les dispositions de pages grâce à la relation d'héritage de template.

Je te propose maintenant d'aborder un dernier sujet très intéressant pour les sites modernes : comment créer des composants réutilisables à l'aide de templates.

L'objectif est de garder une architecture propre et maintenable côté serveur, même lorsqu'on utilise un framework serveur non orienté composants. Tu vas voir en effet que les langages de templating permettent de relever ce défi avec brio !

La problématique

La relation d'héritage est une façon de partager du code entre templates qui est efficace lorsque tous les templates dérivés ont besoin de la totalité du code du template parent. Dans l'article précédent, nous avons vu l'exemple typique d'un template parent contenant un en-tête et un pied de page commun à toutes les pages du site.

Mais prenons maintenant l'exemple d'un blog sur lequel plusieurs auteurs publient des articles. Supposons qu'on veuille afficher une courte présentation de l'auteur au bas de chaque article, et créer une page affichant tous les auteurs du blog.

Composition de templates

Créer un ancêtre commun aux templates d'article et de liste d'auteurs ne répondrait pas au besoin d'afficher plusieurs auteurs dans la même page.

On a donc ici clairement besoin d'un template indépendant modélisant un auteur pour pouvoir l'intégrer dans les templates d'article et de liste d'auteurs. On utilise dans ce cas une relation de composition.

Tip

La composition de templates convient bien dans les cas suivants :

  • pour des morceaux de templates répétés plusieurs fois dans une même page
  • pour des morceaux de templates présents dans plusieurs types de pages, mais pas tous.
  • pour remplacer l'héritage dans les langages de templating qui n'implémentent pas cette relation.

Il y existe 2 types de templates servant à faire de la composition :

Les templates partiels

Un template partiel n'est ni plus ni moins qu'un morceau de template stocké dans un fichier indépendant. Voici l'exemple du template partiel author.njk représentant un auteur :

<div class="author">
   <img src="/public/img/" alt="" class="author-photo"/>
   <div>
      <p class="author-name"></p>
      <p class="author-bio"></p>
   </div>
</div>

Pour intégrer ce template partiel dans le template d'article, j'ajoute le code suivant à l'endroit souhaité dans le template d'article (début ou fin par exemple) :


{% set author=metadata.authors | getAuthorById(authorId) %}
{% include "components/author.njk" %}

La première ligne récupère l'auteur en filtrant la collection metadata.authors sur l'id de l'auteur représenté par la propriété authorId de l'article. Avec l'instruction set elle stocke l'auteur dans la variable author, qui est bien celle qu'on utilise dans le composant author.njk.

Important

Le point important à bien noter ici est que le template d'auteur a accès au contexte du template d'article et donc à l'auteur. À la différence d'un composant, un template partiel ne possède pas son propre contexte de données, mais partage celui de son hôte.

Lors de la génération des pages HTML à partir des templates, chaque page d'article intégrera le code généré par le template partiel.

Warning

Les templates partiels sont une technique simple pour centraliser du contenu statique. Mais le fait qu'ils partagent le contexte de leur hôte et ne soient pas paramétrables, en réduit beaucoup l'intérêt. En effet, cela crée un couplage fort entre les templates partiels et leurs hôtes, ce qui va à l'encontre d'une architecture modulaire et facile à maintenir sur le long terme.

Les macros (= composants)

Pour éviter le problème de couplage entre les templates partiels et leurs hôtes, certains langages de templates modernes proposent la notion de macro, qui est très semblable à celle de composants qu'on trouve dans les frameworks comme Nuxt.js.

Exemple

Reprenons notre template partiel author.njk représentant un auteur, et transformons-le en macro :


{%- macro Render(author) %}

<div class="author">
   <img src="/public/img/{{ author.photo }}" alt="{{ author.name }}" class="author-photo"/>
   <div>
      <p class="author-name">{{ author.name }}</p>
      <p class="author-bio">{{ author.bio }}</p>
   </div>
</div>

{% endmacro %}

Une macro est équivalente à une fonction et peut donc recevoir des paramètres. Ici, la macro Render reçoit l'objet représentant l'auteur en paramètre.

Pour utiliser cette macro dans le template d'article :


{% set author=metadata.authors | getAuthorById(authorId) %}
{% import "../components/author.njk" as Author %}
{{ Author.Render(author) }}

Important

Le point clé : la macro ne partage pas le contexte du template hôte, mais reçoit en paramètre les données dont elle a besoin. Il n'y a donc aucun couplage entre les deux.

Remarque : on peut placer le code CSS du composant dans un fichier author.css et l'importer dans le template de base avec la syntaxe classique :

{% include "../components/author.css" %}

Ainsi le code CSS du composant sera partagé entre toutes ses instances si l'on utilise le composant à plusieurs endroits.

Avantages des macros par rapport aux templates partiels

Les macros :

De plus, comme les templates partiels, les macros peuvent être imbriquées, c'est-à-dire s'appeler les unes les autres.

Ainsi, même avec des frameworks serveur sans notion de composant native, comme Flask (Python), Symfony (PHP), Jekyll (Ruby), Express.js et Eleventy (basés sur Node.js), il est possible de créer du code propre et modulaire grâce aux macros.

Note

Les macros sont particulièrement adaptées au rendu serveur statique. Elles ne remplacent pas la notion de composant côté client introduite par React.

Quels langages de templating permettent de créer des composants ?

Les langages de templating les plus évolués possèdent presque tous la notion de composant, qu'elle soit nommée composant, macro, mixin ou fonction, comme le montre le tableau suivant :

Langage de
template
Framework Implémentation
des composants
Syntaxe type
Nunjucks Node.js macros {% macro %}/{{ macro() }}
Jinja2 Flask, Django macros identique à Nunjucks
Twig Symfony macros identique à Nunjucks
Blade Laravel composants <x-component>
Pug Node.js mixins mixin/+mixin()
EJS Express.js fonctions JS <% function() { %>

Conclusion

Maîtriser un langage de templating puissant est un atout majeur pour créer du code propre, modulaire et facile à lire et à maintenir. En particulier, les composants permettent de construire une architecture de code modulaire avec les frameworks serveur qui ne fournissent pas nativement la notion de composant.

Pour mon blog statique, j'utilise le langage de templating Nunjucks avec le framework Eleventy. J'ai découvert les deux en même temps, et il m'a fallu quelques jours pour apprivoiser la syntaxe Nunjucks, mais j'apprécie maintenant beaucoup sa puissance et son expressivité.

J'utilise l'héritage de template pour les layouts, et des composants pour les fils d'Ariane et les résumés d'articles (sur les pages de catégories d'articles). Mon architecture de code reste simple et propre, et je peux gérer des problématiques complexes telles que la traduction et la taxonomie de façon bien plus simple, personnalisable et performante qu'avec WordPress. C'est un vrai régal !

Pour finir, je serais curieux de savoir quels langages de templating tu utilises et si tu en es satisfait. Dis-moi tout en commentaire ! Et si cet article t'a été utile, merci de le liker pour aider à le faire connaître. 😉