Hugo テンプレート内で define による部分テンプレート定義を行う(関数もどき)

Hugo のテンプレートファイル内で define を使用すると、部分的なテンプレートを定義することができ、別の場所から関数のように呼び出すことができます。

define による部分テンプレート定義の基本

Hugo のテンプレート定義は、基本は layouts ディレクトリ内に置いたテンプレートファイル(HTML ファイル)単位で行うのですが、そのテンプレートファイルの中で、define アクションを使用することで、入れ子でテンプレート定義を行うことができます。 テンプレートファイル内で使える関数定義のようなもの だと思うと分かりやすいです。

次の例では、define を使用して、showParentSection というテンプレートを定義しています。

showParentSection テンプレートを定義する
{{/* セクションの一覧を出力します */}}
{{ define "showParentSection" }}
  <h2>セクション一覧</h2>
  <ul>
    {{- range .Site.Sections }}
      <li><a href="{{ .RelPermalink }}">{{ .Title }}</a>
    {{- end }}
  </ul>
{{ end }}

define で定義したテンプレートを呼び出すには、template 関数を使用します。

{{ template "showParentSection" . }}

2 番目のパラメータで渡した値が、部分テンプレート側のドットコンテキスト (.) として参照できるようになります。 多くの場合は、上記のようにドット (.) を指定し、Page オブジェクトを参照できるようにします。

複数のパラメータを受け取る部分テンプレートを定義する

template 関数を使って部分テンプレートを呼び出すときは、引数に . を指定して Page オブジェクトを渡すことが多いのですが、単純な文字列や数値をパラメータとして渡すこともできます。 次の例では、パラメータで渡された名前を使って挨拶文を出力する greet テンプレートを定義しています。

greet テンプレートを定義する
{{ define "greet" }}
  Hello, {{ . }}!
{{ end }}

この greet テンプレートを呼び出すには次のようにします。

{{ template "greet" "Maku" }}
出力結果
Hello, Maku!

パラメータとして マップオブジェクト を渡せば、名前付きパラメータを実現できます。 次の例で定義している calc テンプレートは、xy というキーを含むマップオブジェクトを受け取ることを想定しています。

{{ define "calc" }}
  {{ printf "%d+%d=%d" .x .y (add .x .y) }}
{{ end }}

この calc テンプレートは次のように呼び出します。 マップオブジェクトを作成するときは、dict 関数のパラメータに、キーと値を交互に指定します。

{{ template "calc" (dict "x" 100 "y" 200) }}
出力結果
100+200=300
☝️ ワンポイント

マップオブジェクトを受け取る部分テンプレートを定義するときは、次のように先頭でローカル変数($x$y)に代入してしまう慣例もあるようです。 このように記述しておくことで、この部分テンプレートにどのようなキーを持つマップオブジェクトを渡せばよいのかが一目で分かるようになります。

{{ define "calc" }}
  {{ $x := .x }}
  {{ $y := .y }}
  {{ printf "%d+%d=%d" $x $y (add $x $y) }}
{{ end }}

パーシャルテンプレートとの違い

layouts/partials/foo.html というファイル名で作成したパーシャルテンプレートは、次のように partial を使って呼び出します。

{{ partial "foo" . }}

一方で、define で定義した部分テンプレート(Go のドキュメントでは nested template と呼んでいます)は、次のように template を使って呼び出します。

{{ template "foo" . }}

呼び出し方がちょっと異なるだけで、ほとんど同じような感覚で使えるようです。 使い分けの基準としては、

  • 複数のテンプレートファイルから共通で使用するものは、パーシャルテンプレート (layouts/partials/xxx.html) として定義する。
  • 1 つのテンプレートファイル内でのみ使用する部分テンプレートは、define による部分テンプレートとして定義する。

と考えておけばよいでしょう。