更新履歴
- : 公開
TL;DR
常にトップレベルを指す特殊変数 $
を使えばよい。
はじめに
Go には、標準ライブラリにテンプレートライブラリ text/template
がある。 この text/template
における制御構造、with
と range
は次のように使われる。
# {{ .Title }}
# User
{{ with .User }}
{{ .Name }} ({{ .ID }})
{{ end }}
# Items
{{ range .Items }}
- {{ . }}
{{ end }}
text/template
の .
は、現在の操作対象を表す特殊なオブジェクトである。
with
や range
は、.
を変更する効果を持つ。 with
は引数に渡されたオブジェクトを .
へセットして、内部のテンプレートを実行する。 range
は引数に渡されたイテレート可能なオブジェクトに対し、それぞれの要素を .
へセットして、要素の個数だけ内部のテンプレートを実行する。
つまりこのテンプレートは、次のような構造をレンダリングしている (Execute()
の第2引数)。
tmpl.Execute(out, Params{
Title: "foo",
User: User{
ID: 123,
Name: "john",
},
Items: []string{
"hoge",
"piyo",
"fuga",
},
})
やりたいこと
今回おこないたいのは、with
や range
の中で、その外側で使われていたトップレベルのオブジェクトを参照することだ。
{{ with .User }}
ここから .Title を参照するには?
{{ end }}
{{ range .Items }}
ここから .User を参照するには?
{{ end }}
with
や range
は、.
を自身の対象オブジェクトに変更するので、 単に {{ with .User }}
の中で .Title
と書いても、それは User
の Title
プロパティを参照しているとみなされる。
text/template
では変数が使えるので、テンプレートの先頭で
{{ $params := . }}
とでもしておけば実現は可能である。
しかしながら、頻発するシチュエーションにしてはあまりに不恰好である。よりスマートな方法が用意されているはずだ。
解決方法
常にトップレベルを指す特殊変数 $
を使えばよい。
{{ with .User }}
{{ $.Title }}
{{ end }}
{{ range .Items }}
{{ $.User.Name }}
{{ end }}
$
は、テンプレートが実行されるときに渡されたオブジェクトを指す。 これを使えば現在の .
に関係なくトップレベルを参照できる。
このことは、text/template
の公式ドキュメントにも以下のように記載されている。
When execution begins, $ is set to the data argument passed to Execute, that is, to the starting value of dot.