はじめに
Goではhttp/template
を使用してhtmlのテンプレートに値を渡すことができます。
具体的には
- 変数
- スライス
- マップ
- 構造体
などの値です。
今回は、基本となる変数と、使用機会が一番多いであろう構造体について説明していきます。
Web開発に使用する技術ですが、検索しても情報があまり出てこなかったので書きました。
環境
- macOS Catalina 10.15
- go 1.12.9
0.何も渡さず実行
メインのGoとhtmlファイルです。
これらを基本に説明していきます。
.
├── main.go
└── tpls.html
package main
import (
"html/template"
"log"
"os"
)
var tpl *template.Template
func init() {
tpl = template.Must(template.ParseFiles("tpls.html"))
}
func main() {
err := tpl.Execute(os.Stdout, nil) // dataは渡さない
if err != nil {
log.Fatalln(err)
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Golang Template Test</title>
</head>
<body>
<h1>hoge</h1>
</body>
</html>
main.go を go run main.go
で実行した結果がこちらです。
$ go run main.go
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Golang Template Test</title>
</head>
<body>
<h1>hoge</h1>
</body>
</html>⏎
なんの変哲もありませんね。
今回はターミナルに標準出力しているので全文表示されますが、実際にはwebブラウザに読み込ませるので、hoge だけが表示されるはずです。
ではテンプレートの使い方を見ていきましょう。
1.変数の値を渡す
まずは変数を渡します。
tpl.Executeの第2引数に変数を指定することで渡せます。
package main
import (
"html/template"
"log"
"os"
)
var tpl *template.Template
func init() {
tpl = template.Must(template.ParseFiles("tpls.html"))
}
func main() {
var Age = 235
err := tpl.Execute(os.Stdout, Age) // 変数を指定
if err != nil {
log.Fatalln(err)
}
}
次にhtmlです。
値を受け取るには{{.}}
の表記が必要になります。
独特の表記ですが基本となるので慣れてください。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Golang Template Test</title>
</head>
<body>
<h1>{{.}}</h1> <!-- 値を受け取る -->
</body>
</html>
実行結果がこちらです。
$ go run main.go
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Golang Template Test</title>
</head>
<body>
<h1>235</h1>
</body>
</html>⏎
値が渡されているのがわかります。
複数回値を受け取れる
なお、受け取った値は複数回表示したり、変数に代入することができます。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Golang Template Test</title>
</head>
<body>
<h1>{{.}}</h1> <!-- 同じ値を複数回受け取る事もできる -->
<h1>{{.}}</h1>
<h1>{{$Age := .}}</h1> <!-- 代入したときは表示されない -->
<h1>{{$Age}}</h1> <!-- 値を表示 -->
</body>
</html>
$ go run main.go
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Golang Template Test</title>
</head>
<body>
<h1>235</h1>
<h1>235</h1>
<h1></h1>
<h1>235</h1>
</body>
</html>⏎
2.構造体の値を渡す
次に構造体の値を渡します。
実際の開発では構造体を使うことが一番多いでしょう。
具体的にはjsonから受け取った値を構造体に入れて、それを表示するといった感じでしょう。
では、実行して行きます。
package main
import (
"html/template"
"log"
"os"
)
type Person struct {
Name string
Age int
}
var tpl *template.Template
func init() {
tpl = template.Must(template.ParseFiles("tpls.html"))
}
func main() {
p1 := Person{
Name: "hogefuga",
Age: 28,
}
err := tpl.Execute(os.Stdout, p1)
if err != nil {
log.Fatalln(err)
}
}
今回はName
とAge
の2つのフィールドを持った構造体を用意しました。
ベタですね。
htmlはこちらです。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Golang Template Test</title>
</head>
<body>
<h1>{{.}}</h1>
<h2>{{.Name}}</h2> <!-- フィールドにアクセスする -->
<h2>{{.Age}}</h2>
</body>
</html>
フィールドにアクセスするには{{.Name}}
のようにドットに続けてフィールド名を書きます。
一方で、{{.}}
のみだと構造体がそのまま表示されます。
これは感覚そのままですね。
では実行結果です。
$ go run main.go
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Golang Template Test</title>
</head>
<body>
<h1>{hogefuga 28}</h1>
<h2>hogefuga</h2>
<h2>28</h2>
</body>
</html>⏎
全く説明した通りの結果です。
3.構造体のスライスを渡す
実際の開発では、[]hogestruct
として構造体を複数使用したい場合があるでしょう。
こちらのケースも見ていきます。
package main
import (
"html/template"
"log"
"os"
)
type Person struct {
Name string
Age int
}
var tpl *template.Template
func init() {
tpl = template.Must(template.ParseFiles("tpls.html"))
}
func main() {
p1 := Person{
Name: "hogefuga",
Age: 28,
}
p2 := Person{
Name: "hogeeeeeee",
Age: 51,
}
p3 := Person{
Name: "fugagagaga",
Age: 3,
}
var persons = []Person{p1, p2, p3}
err := tpl.Execute(os.Stdout, persons)
if err != nil {
log.Fatalln(err)
}
}
やっているのは構造体のスライスを渡しているだけです。
次にhtmlです。
スライスの全要素を順に表示するには{{range .}}
を使用します。
実際にコードを見るのが速いでしょう。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Golang Template Test</title>
</head>
<body>
<ul>
{{range .}}
<li>{{.Name}} - {{.Age}}</li>
{{end}}
</ul>
</body>
</html>
range
を使用するときは{{end}}
を忘れないようにしてください。
では実行結果です。
$ go run main.go
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Golang Template Test</title>
</head>
<body>
<ul>
<li>hogefuga - 28</li>
<li>hogeeeeeee - 51</li>
<li>fugagagaga - 3</li>
</ul>
</body>
</html>⏎
正しく表示されたと思います。
スライス要素に個別にアクセス
ちなみにスライスの要素に個別にアクセスしたいときは、html内でデフォルト関数のindex
を使用します。
これもコードを見たほうが速いでしょう。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Golang Template Test</title>
</head>
<body>
<ul>
{{index . 1}} <!-- スライスの要素に個別にアクセス -->
{{index . 2}}
{{(index . 0).Name}} <!-- 要素のフィールドにアクセスすることも可能-->
</ul>
</body>
</html>
個別要素のフィールドにアクセスするには{{(index . 0).Name}}
のように()
でくくる必要があります。
それでは実行結果です。
$ go run main.go
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Golang Template Test</title>
</head>
<body>
<ul>
{hogeeeeeee 51}
{fugagagaga 3}
hogefuga
</ul>
</body>
</html>
デフォルト関数はindex
の他にも多数存在します。
詳細はgolangのtemplateページのFunctionの部分を参照してください。
ページがtext/templateとなっていますが、同じように使えます。(html/templateのページで詳細が見当たらなかった)
4.構造体のメソッドについて
templateでは構造体のメソッドを実行することも可能です。
ただしメソッドの実装は値渡しでないといけません。(ポインタ渡しはエラー)
package main
import (
"html/template"
"log"
"os"
)
type Person struct {
Name string
Age int
}
// func (p *Person) のようなポインタ渡しはエラーが出る
func (p Person) DoubleAge() int {
return p.Age * 2
}
var tpl *template.Template
func init() {
tpl = template.Must(template.ParseFiles("tpls.html"))
}
func main() {
p1 := Person{
Name: "hogefuga",
Age: 28,
}
err := tpl.Execute(os.Stdout, p1)
if err != nil {
log.Fatalln(err)
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Golang Template Test</title>
</head>
<body>
{{.DoubleAge}} <!-- methodも書き方は同じ -->
</body>
</html>
実行結果です。
$ go run main.go
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Golang Template Test</title>
</head>
<body>
<ul>
56
</ul>
</body>
</html>⏎
Ageを2倍した値が表示されます。
おわりに
今回の記事は以上になります。
基本的な開発はこの記事の内容で十分行えると思います。
html/template
には関数を渡すfuncmap
や、template内でパイプ|
を使用する方法など、他にも様々な手法があります。
反響があれば記事を書こうと思います。
最後まで読んでいただきありがとうございました。