summaryrefslogtreecommitdiffhomepage
path: root/vhosts/blog/content
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2024-08-19 02:08:27 +0900
committernsfisis <nsfisis@gmail.com>2024-08-19 02:24:06 +0900
commit41203279e88ceb19e50fc47b9781cd57afc696cc (patch)
tree4506fb5404c431413394fcc9e08230d214280fec /vhosts/blog/content
parent5f8709982bd83ab2abba68c00b33f6c1494d5d0b (diff)
downloadnsfisis.dev-41203279e88ceb19e50fc47b9781cd57afc696cc.tar.gz
nsfisis.dev-41203279e88ceb19e50fc47b9781cd57afc696cc.tar.zst
nsfisis.dev-41203279e88ceb19e50fc47b9781cd57afc696cc.zip
feat(blog/content): new post /posts/2024-08-19/go-template-access-outer-scope-pipeline-within-with-or-range/
Diffstat (limited to 'vhosts/blog/content')
-rw-r--r--vhosts/blog/content/posts/2024-08-19/go-template-access-outer-scope-pipeline-within-with-or-range.ndoc141
1 files changed, 141 insertions, 0 deletions
diff --git a/vhosts/blog/content/posts/2024-08-19/go-template-access-outer-scope-pipeline-within-with-or-range.ndoc b/vhosts/blog/content/posts/2024-08-19/go-template-access-outer-scope-pipeline-within-with-or-range.ndoc
new file mode 100644
index 00000000..ee7b0ab8
--- /dev/null
+++ b/vhosts/blog/content/posts/2024-08-19/go-template-access-outer-scope-pipeline-within-with-or-range.ndoc
@@ -0,0 +1,141 @@
+---
+[article]
+uuid = "eed112e4-3227-4b3f-9991-7e11c288ee2b"
+title = "【Go】 text/template の with や range の内側から外側の \".\" にアクセスする"
+description = "Go言語の text/template における with や range は \".\" を上書きする。これらの内側から外側の \".\" にアクセスする方法を調べた。"
+tags = [
+ "go",
+]
+
+[[article.revisions]]
+date = "2024-08-19"
+remark = "公開"
+---
+<article>
+ <section id="tldr">
+ <h>TL;DR</h>
+ <p>
+ 常にトップレベルを指す特殊変数 <code>$</code> を使えばよい。
+ </p>
+ </section>
+ <section id="intro">
+ <h>はじめに</h>
+ <p>
+ Go には、標準ライブラリにテンプレートライブラリ <code>text/template</code> がある。
+ この <code>text/template</code> における制御構造、<code>with</code> と <code>range</code> は次のように使われる。
+ </p>
+ <codeblock>
+ <![CDATA[
+ # {{ .Title }}
+
+ # User
+
+ {{ with .User }}
+ {{ .Name }} ({{ .ID }})
+ {{ end }}
+
+ # Items
+
+ {{ range .Items }}
+ - {{ . }}
+ {{ end }}
+ ]]>
+ </codeblock>
+ <p>
+ <code>text/template</code> の <code>.</code> は、現在の操作対象を表す特殊なオブジェクトである。
+ </p>
+ <p>
+ <code>with</code> や <code>range</code> は、<code>.</code> を変更する効果を持つ。
+ <code>with</code> は引数に渡されたオブジェクトを <code>.</code> へセットして、内部のテンプレートを実行する。
+ <code>range</code> は引数に渡されたイテレート可能なオブジェクトに対し、それぞれの要素を <code>.</code> へセットして、要素の個数だけ内部のテンプレートを実行する。
+ </p>
+ <p>
+ つまりこのテンプレートは、次のような構造をレンダリングしている (<code>Execute()</code> の第2引数)。
+ </p>
+ <codeblock language="go">
+ <![CDATA[
+ tmpl.Execute(out, Params{
+ Title: "foo",
+ User: User{
+ ID: 123,
+ Name: "john",
+ },
+ Items: []string{
+ "hoge",
+ "piyo",
+ "fuga",
+ },
+ })
+ ]]>
+ </codeblock>
+ </section>
+ <section id="what-i-want-to-do">
+ <h>やりたいこと</h>
+ <p>
+ 今回おこないたいのは、<code>with</code> や <code>range</code> の中で、その外側で使われていたトップレベルのオブジェクトを参照することだ。
+ </p>
+ <codeblock>
+ <![CDATA[
+ {{ with .User }}
+ ここから .Title を参照するには?
+ {{ end }}
+
+ {{ range .Items }}
+ ここから .User を参照するには?
+ {{ end }}
+ ]]>
+ </codeblock>
+ <p>
+ <code>with</code> や <code>range</code> は、<code>.</code> を自身の対象オブジェクトに変更するので、
+ 単に <code>{{ with .User }}</code> の中で <code>.Title</code> と書いても、それは <code>User</code> の <code>Title</code> プロパティを参照しているとみなされる。
+ </p>
+ <p>
+ <code>text/template</code> では変数が使えるので、テンプレートの先頭で
+ </p>
+ <codeblock>
+ <![CDATA[
+ {{ $params := . }}
+ ]]>
+ </codeblock>
+ <p>
+ とでもしておけば実現は可能である。
+ </p>
+ <p>
+ しかしながら、頻発するシチュエーションにしてはあまりに不恰好である。よりスマートな方法が用意されているはずだ。
+ </p>
+ </section>
+ <section id="solution">
+ <h>解決方法</h>
+ <p>
+ 常にトップレベルを指す特殊変数 <code>$</code> を使えばよい。
+ </p>
+ <codeblock>
+ <![CDATA[
+ {{ with .User }}
+ {{ $.Title }}
+ {{ end }}
+
+ {{ range .Items }}
+ {{ $.User.Name }}
+ {{ end }}
+ ]]>
+ </codeblock>
+ <p>
+ <code>$</code> は、テンプレートが実行されるときに渡されたオブジェクトを指す。
+ これを使えば現在の <code>.</code> に関係なくトップレベルを参照できる。
+ </p>
+ <p>
+ このことは、<a href="https://pkg.go.dev/text/template#hdr-Variables"><code>text/template</code> の公式ドキュメント</a>にも以下のように記載されている。
+ </p>
+ <blockquote>
+ When execution begins, $ is set to the data argument passed to Execute, that is, to the starting value of dot.
+ </blockquote>
+ </section>
+ <section id="reference">
+ <h>参考</h>
+ <ul>
+ <li><a href="https://stackoverflow.com/questions/14800204/in-a-template-how-do-you-access-an-outer-scope-while-inside-of-a-with-or-rang">直接の出典である Stack Overflow の回答: "In a template how do you access an outer scope while inside of a "with" or "range" scope?"</a></li>
+ <li><a href="https://pkg.go.dev/text/template#hdr-Variables">大元の出典である <code>text/template</code> の公式ドキュメント</a></li>
+ </ul>
+ </section>
+</article>