Article original : Hugo, the scope, the context and the dot de Régis Philibert

Le contexte, c’est un concept assez perturbant quand on commence à vouloir développer des modèles de page pour Hugo. Il est facile de s’emmêler les pinceaux pour accéder à ses variables. Au travers de quelques exemples très simples, Régis Philibert se propose de nous aider à y voir plus clair.

Mais pourquoi ma variable n’est pas accessible ici et ici ? 🙄

Quand est un habitué des bons vieux langages de templates où le périmètre fonctionnel est rarement un problème, bien comprendre les contraintes de périmètre en Go Template n’est pas toujours aisé.

Dans cet article, nous essaierons de comprendre l’impact du périmètre et du contexte à l’intérieur de nos gabarits et de nos fichiers partiels, et comment jongler avec le point 🤹.

Le contexte et le point

J’utilise ici le mot périmètre dans le titre, car c’est la première chose qui vient à l’esprit quand on fait face à cette problématique et j’imagine que c’est le terme que les gens vont utiliser pour rechercher de l’aide. Mais en définitive c’est plutôt du contexte dont nous allons parler ici.

Le périmètre c’est ce qui est disponible dans une situation donnée dans votre code. À l’intérieur d’une classe ou d’une fonction par exemple.

Mais dans les gabarits d’Hugo, la plupart du temps, vous n’avez accès qu’à un seul objet : le contexte. Et il est stocké dans un point.

Oui, ce point-là. {{.}}

Et vous finissez donc par utiliser les propriétés de cet objet comme ça :
.Title, .Permalink, .IsHome

Le point de la page

Le contexte d’origine, celui disponible dans votre fichier de gabarit racine baseof.html et dans les autres fichiers de gabarits sera toujours le contexte de la page. En fait, tout ce que vous avez besoin d’afficher dans cette page est contenu dans ce point.
.Title, .Permalink, .Resources et tout ce qui vous chante.

Même les informations de votre site sont stockées dans le contexte de page à l’aide de .Site qui est prêt à l’emploi.

Mais en Go Template dès que vous utilisez une fonction, vous perdez ce contexte, et votre précieux point, votre contexte est remplacé par celui de la fonction, qui a son propre… point.

Si par exemple dans mon gabarit de page j’ai :

With

{{ with .Title }}
    {{/* Maintenant le point c’est .Title */}}
    <h1>{{ . }}</h1>
{{ end }}

À l’intérieur de ce with vous n’êtes plus dans le contexte de page. Le contexte, le point, c’est maintenant le titre de votre page. Ici, c’est exactement ce que nous voulons !

Range

Même chose ici, une fois que vous avez commencé à itérer avec range, le contexte est l’élément actuellement parcouru. Vous perdez le contexte de page au profit du contexte de la fonction range.

{{ range .Data.Pages }}
    {{/* Ici le point est celui de la page 'en cours'. */}}
    {{ .Permalink }}
{{ end }}
{{ range .Resources.Match "gallery/*" }}
    {{/* Ici le point designe une des images. */}}
    {{ .Permalink }}
// {{ end }}
{{ range (slice "Hello" "Bonjour" "Gutten Tag") }}
    {{/* Ici le point désigne cette chaîne de caractères. */}}
    {{ . }}
{{ end }}

Le contexte du plus haut niveau de la page 💲

Heureusement pour nous, Hugo stocke le contexte de page dans un $ donc cela ne fait rien si vous vous trouvez au fin fond d’un with ou d’un range, vous pouvez toujours récupérer le contexte du plus haut niveau de la page.

Un niveau d’imbrication

{{ with .Title }}
    {{/* Le point désigne .Title */}}
    <h1>{{ . }}</h1>
    {{/* $ désigne le plus haut niveau de la page */}}
    <h3>From {{ $.Title }}</h3>
{{ end }}

Trois niveaux d’imbrication

{{/* 1. Le point désigne le plus haut niveau de la page (de liste) */}}
<h1>{{ .Title }}</h1>
{{ range .Data.Pages }}
    <article>
        {{/* 2. Le point désigne la page en cours */}}
        <h3>{{ .Title }}</h3>
        <hr>
        {{ range .Resources.Match "images/.*" }}
            <figure>
                {{/* 3. Le point désigne une de ces ressources */}}
                <img src="{{ .Permalink }}">
                {{/* $ désigne le contexte de haut niveau de la page */}}
                <caption>{{ .Title }} de l’article {{ $.Title }}</caption>
            </figure>
        {{ end }}
    </article>
{{ end }}

Les fichiers partiels

Par défaut, les fichiers partiels ne passent aucun contexte.
Mais il suffit d’un seul paramètre pour y remédier. Cet objet sera alors disponible à l’intérieur du fichier partiel et sera référencé à l’aide, vous l’aviez deviné, du point.

Donc pour des fichiers partiels simples, vous n’aurez besoin que du contexte de page. Le point de votre page.

    {{ partial "page/head" . }}

Ici la fonction partial a pour paramètre votre contexte, à priori celui de votre page si vous n’êtes pas dans une boucle range ou dans une condition with ou bien dans un autre fichier partiel.

    <h1>
        {{ .Title }}
    </h1>
    <h3><time datetime="{{ .Date }}">{{ dateFormat "Écrit le 2 January 2006" .Date }}</time></h3>

Maintenant, imaginons que vous écriviez un fichier partiel pour le rendu de votre image fantaisiste encadrée, vous n’avez besoin que de son chemin, qui devient donc votre contexte.

{{ partial "img" $path }}

Dans le fichier partials/img.html, on aura donc :

<figure class="Figure Figure--framed">
    <img src="{{ . }}" alt="">
</figure>

Le point ici c’est la valeur de $path.

C’est un exemple tout simple. La plupart du temps, vous aurez besoin de beaucoup plus de valeurs.

Pas de souci, nous pouvons utiliser la fonction dict pour passer un ensemble d’éléments en paramètre (entier, chaine de caractère, objet)

dict va créer une map, souvent désignée également comme un tableau associatif.
Reportez-vous à la documentation de cette fonction ou à mon propre article sur le sujet.

{{ partial "img" dict("path" $path "alt" "Nice blue sky") }}

Le point va contenir cet objet à l’intérieur du fichier partiel, donc nous pouvons préfixer nos clefs avec .

<figure class="Figure Figure--framed">
    <img src="{{ .path }}" alt="{{ .alt }}">
</figure>

Vous pouvez mettre une lettre majuscule à vos clefs pour qu’elles fassent plus « Hugo » mais j’aime bien mettre tout en minuscules. De cette manière dans un fichier partiel j’identifie immédiatement les clefs issues d’un contexte personnalisé de celles issues de celui de la page.

Accéder au plus haut niveau $ depuis un fichier partiel

Contrairement à range et with le contexte de page n’est pas disponible dans $.

Pas de problème, nous allons ajouter le contexte de la page à notre dict.

Vous pouvez appeler cette clef importante comme vous voulez, beaucoup de gens utilisent « Page » pour pouvoir écrire Page.Title. Comme ça vous chante, du moment que vous êtes consistent dans votre nomenclature.

{{ partial "img" dict("Page" . "path" $path "alt" "Nice blue sky") }}
<figure class="Figure Figure--framed">
    <img src="{{ .path }}" alt="{{ .alt }} from {{ .Page.Title }}">
</figure>

Conclusion

Ce point peut vite devenir votre meilleur ami et s’avère bien pratique une fois qu’on a appris à jongler avec. Il permet d’écrire du code très lisible même si parfois on ne sait plus très bien à quel niveau on se situe.

Il y a d’autres fonctions qui prennent le contexte, regardez notamment du côté de block et template.

Bonne mise au point !