Open25
Hugoテンプレートしたい
Templates
- Hugoのテンプレート操作について、きちんと理解する
- とくにGoの書き方なのか、Goテンプレートの書き方なのかを、区別したい
- 同じ処理をPythonで書くとどうなるかを考えてみる
Introduction to templating
- HugoではGoの
text/template
とhtml/template
パッケージを使っている
HTMLテンプレートの例
{{ $v1 := 6 }}
{{ $v2 := 7 }}
<p>The product of {{ $v1 }} and {{ $v2 }} is {{ mul $v1 $v2 }}.</p>
- (コードを書くときに
go
にすればいいのか、html
にしたほうがいいのか?) - Goテンプレートでは
{{ }}
の中で、Go処理ができる
テンプレート変数の作成
-
:=
で変数を作成できる
短縮変数宣言
v1 := 6
- 明示的な宣言
- Goは静的型付け言語なので、変数の型宣言が必要
明示的な変数宣言
var v1 int = 6
- テンプレート構文で使う場合、変数名の先頭に
$
が必要
テンプレート構文
{{ $v1 := 6 }}
<p>The value of v1 is {{ $v1 }}</p>
- Pythonと比較
v1 = 6
テンプレート変数の演算
- テンプレート構文の中で、計算などができる
{{ $v1 := 6 }}
{{ $v2:=7 }}
<p>{{ $v1 }} と {{ $v2 }} の積は {{ mul $v1 $v2 }} です</p>
- 演算の引数はスペースで区切るみたい
{{ 関数名 引数1 引数2 ...}}
- 利用できる関数は以下を参照
if文
{{ $v1 := 10 }}
{{ if eq $v1 10 }}
v1 is 10
{{ else }}
v1 is not 10
{{ end }}
- テンプレート構文の中で
if
文が使える -
{{if 条件}} ... {{ else }} ... {{ end }}
の形 - 条件で比較演算子や論理演算子が使える
- 通常のGoで書くとこうなる
v1 := 10 // 短縮変数宣言
if v1 == 10 {
fmt.Println("v1 is 10")
} else {
fmt.Println("v1 is not 10")
}
- Pythonで書くとこうなる
v1 = 10
if v1 == 10:
print("v1 is 10")
else:
print("v1 is not 10")
Context
- コンテキスト(context)は、テンプレートに渡されるデータのこと
- Hugoのテンプレート作成で、とくに重要な概念
- ひとつのページの内容は
Page
オブジェクトに格納されている - このページ用テンプレートは
Page
オブジェクトの中にあるデータ(=コンテクスト)にアクセスして、レンダリングする
Current Context
<h2>{{ .Title }}</h2>
-
.
で現在のコンテキストにアクセスできる- この場合の
.
はPage
オブジェクトを指す
- この場合の
-
.Title
で現在のコンテキストにあるTitle
の値を取得できる-
Page
オブジェクトのタイトルは、ページのfrontmatterで設定されている値
-
- 処理のスコープによって
.
が指すオブジェクトは変わる
<h2>{{ .Title }}</h2>
{{ range slice "foo" "bar" }}
<p>{{ . }}</p>
{{ end }}
{{ with "baz" }}
<p>{{ . }}</p>
{{ end }}
- スライス(=可変長配列)の処理をGoで書くとこうなる
slice := []string{"foo", "bar"}
for _, value := range slice {
fmt.Println("<p>%s</p>\n", value)
}
- Pythonで書くとこうなる
slice = ["foo", "bar"]
for value in slice:
print(f"<p>{value}</p>")
-
{{ slice "foo" "bar" }}
が["foo", "bar"]
に見えるようにならないといけない
Template Context
- コンテキストのスコープについて
- ブロックの中のコンテキストは
.
で取得できる - ブロックの外のコンテクスト(=ファイルのコンテキスト)は
$.
で取得できる
- ブロックの中のコンテキストは
- Go言語自体で、変数のスコープを小さくすることが推奨されている
- テンプレートをいじるときも、スコープを意識すればOK
{{ with "foo" }}
<p>{{ $.Title }} - {{ . }}</p>
{{ end }}
<p>Page Title - foo</p>
-
{{ . }}
はブロック内のコンテキストなので"foo"
となる -
{{ $.Title }}
は、ブロック外のコンテキストにアクセスしている。この場合、ブロック外はPage
オブジェクトなので、Page.Title
が取得できる
Actions
{{ $convertToLower := true }}
{{ if $convertToLower }}
<h2>{{ strings.ToLower .Title }}</h2>
{{ else }}
<h2>{{ .Title }}</h2>
{{ end }}
Whitespace
-
{{ }}
テンプレート処理したブロックは改行され、空行として残る -
{{- -}}
を使うと、その空行をトリムできる
{{- $convertToLower := true -}}
{{- if $convertToLower -}}
<h2>{{ strings.ToLower .Title }}</h2>
{{- end -}}
Pipes
- テンプレート関数はパイプ処理できる
// "Hugo" を引数として使っている感じ
{{ strings.ToLower "Hugo" }}
// "Hugo"という結果をパイプで渡している感じ
{{ "Hugo" | strings.ToLower }}
- 同じ結果が得られるので、どちらをつかってもよい(はず)
- Hugoではパイプ処理を推奨、ということもないはず
Line splitting
{{ $v := or .Site.Language.LanguageName .Site.Language.Lang }}
// こう書いてもOK
{{ $v := or
.Site.Language.LanguageName
.Site.Language.Lang
}}
Variables
-
:=
で変数を初期化する -
=
で、定義済みの変数に代入する
{{ $total := 3 }}
{{ range slice 7 11 21 }}
{{ $total = add $total . }}
{{ end }}
{{ $total }}
- Goで書くとこうなる
total := 3
slices := []int{7, 11, 21}
for _, num := range slices {
total = total + num
}
fmt.Println(total)
- Pythonで書くとこうなる
total = 3
for i in [7, 11, 21]:
total = total + i
print(total)
- スライス(=可変長配列)から要素を取得する
{{ $slice := slice "foo" "bar" "baz" }}
{{ index $slice 2}} // -> baz
- 辞書型配列からキーを指定して、要素を取得する
{{ $map := dict "a" "foo" "b" "bar" "c" "baz" }}
{{ index $map "c" }} // -> "baz"
-
dict "a" "foo" "b" "bar" ...
を辞書型と読むのは、慣れが必要そう
- Pythonで書くとこうなる
map = {
"a": "foo",
"b": "bar",
"c": "baz",
}
map["c'"]
# もしくは map.get("c")
オブジェクトの再代入
-
Page
オブジェクトやSite
オブジェクトなどを、好きな変数に代入できる - 代入した変数は、元のオブジェクトの値やメソッドにアクセスできる
{{ $homepage := .Site.Home }}
{{ $homepate.Title }} // <- .Site.Home.Title
Comments
- コメントはCスタイル(
/* ... */
) - インラインとブロックで書くことができる
- コメント前後の空行をトリムできる
インライン・コメント
{{/* これはインライン・コメント */}}
{{- /*これは余白なしのインライン・コメント */ -}}
ブロック・コメント
{{/*
これは
ブロック
コメント
*/}}
{{- /*
これは余白を削除した
ブロックコメント
*/ -}}
HTMLコメント
-
safeHTML
すると、出力したHTMLにコメントを残すことができる
{{ "<!-- これはHTMLに残すコメント -->" | safeHTML }}
-
printf
で文字列フォーマットできる
{{ printf "<!-- これは %s に残すコメント -->" .Site.Title | safeHTML }}
Methods
コンテキストのオブジェクトごとに使えるメソッドの一覧
-
Page
オブジェクト、Site
オブジェクトがよく使われる
layouts/_default/single.html
<h1>{{ .Page.Title }} | {{ .Site.Title }}</h1>
- このテンプレートは基本が
Page
オブジェクトなので、.Title
でもOK
layouts/_default/single.html
<h1>{{ .Title }} | {{ .Site.Title }}</h1>
-
.Page.GetPage パス
- 指定したパスの
Page
オブジェクトを取得 - パスは現在のディレクトリからの相対パス、もしくは
-
ContentDir
ディレクトリからのパスで指定する
- 指定したパスの
layouts/_default/single.html
{{ $page := .Page.GetPage "/books/les-miserables" }}
{{ $page.Title }}
Contitional Blocks
Logical Operators
{{ $v1 := true }}
{{ $v2 := false }}
{{ $v3 := false }}
{{ $result := false }}
- AND
{{ if and $v1 $v2 $v3 }}
{{ $result = true }}
{{ end }}
{{ $result }} // -> false
- OR
{{ if or $v1 $v2 $v4 }}
{{ $result = true }}
{{ end }}
{{ $result }} // -> true
Loops
{{ $slices := slice "foo" "bar" "baz" }}
{{ range $slices }}
<p>{{ . }}</p>
{{ else }}
<p>The collection is empty</p>
{{ end }}
- Pythonで書くとこうなる
slices = ["foo", "bar", "baz"]
for s in slices:
print(f"<p>{s}</p>")
{{ $total := 0 }}
{{ range seq 4 }}
{{ $total = add $total . }}
{{ end }}
{{ $total }} // -> 10
- Pythonで書くとこうなる
total = 0
for i in range(4):
total = total + i
print(total)