Go言語によるWebサーバー作成入門 ーテンプレートー5

3 min読了の目安(約3500字TECH技術記事

(作成2020.10.22)
前回の記事はこちら

テンプレートのテンプレート

ディレクトリ構成は次のようにしています。テンプレートを保存するディレクトリを作成して、メインとなるテンプレートと、テンプレートのテンプレートになるmyheader.gohtmlを作成しています。

|--main.go
|--templates
|  |--index.gohtml
|  |--myheader.gohtml

メインとなるテンプレートに{{template "myheader"}}と記述します。

index.gohtml
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Documents</title>
</head>
<body>
<p>{{template "myheader"}}</p>
<h1>Hello, {{.}}!</h1>
</body>
</html>

そして、この{{template "myheader"}}に挿入する内容をmyheader.gohtmlファイルに{{define "myheader"}}{{end}}で囲って記述します。

myheader.gohtml
{{define "myheader"}}
これは<myheader>の記述です。
{{end}}

goファイルは次のようにします。

main.go
package main

import (
	"log"
	"os"
	"text/template"
)

var tpl *template.Template

func init() {
	tpl = template.Must(template.ParseGlob("templates/*.gohtml"))
}

func main() {
	word := "golang"

	nf, err := os.Create("index.html")
	if err != nil {
		log.Fatalln(err)
	}
	defer nf.Close()

	err = tpl.ExecuteTemplate(nf, "index.gohtml", word)
	if err != nil {
		log.Fatalln(err)
	}
}

main.goを実行すると2つのテンプレートが反映されたindex.htmlが作成されます。

テンプレートのテンプレートに値を渡せます

{{template "myheader" .}}として値を後続に渡すことができます。

index.gohtml
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Documents</title>
</head>
<body>
    <h1>main.goから渡された変数: {{.}}</h1> <!-- main.goから渡された変数を表示 -->
    <p>{{template "myheader" .}}</p>    <!-- myheader.gohtmlに変数を渡す -->
</body>
</html>
myheader.gohtml
{{define "myheader"}}
これは<myheader>の記述です。<br>
index.gohtmlから渡された変数:{{.}}
{{end}}
main.go
package main

import (
	"log"
	"os"
	"text/template"
)

var tpl *template.Template

func init() {
	tpl = template.Must(template.ParseGlob("templates/*.gohtml"))
}

func main() {
	word := "golang"

	nf, err := os.Create("index.html")
	if err != nil {
		log.Fatalln(err)
	}

	err = tpl.ExecuteTemplate(nf, "index.gohtml", word)
	if err != nil {
		log.Fatalln(err)
	}
}

出力されるindex.html

実践的な使い方

実際のサーバー運営をイメージすると、HTMLのヘッダーやフッターは決まり文句なので改修することはあまりなく本文とは切り離しておきたいですよね。これまでの総復習的な意味合いを込めて作成例を挙げておきます。

templates/header.gohtml
{{define "header"}}
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>{{.Title}}</title>
</head>
<body>
{{end}}
templates/footer.gohtml
{{define "footer"}}
<div>Copyright &copy; hiroyuki ichijo</div>
</body>
</html>
{{end}}

手当をするメイン部分は次のように簡潔になります。

templates/tpl.gohtml
{{template "header" .}}
<h1>{{.Message}}</h1>
<h2>現在の時刻:{{.Time.Format "2006/1/2 15:04:05"}}</h2>
{{template "footer"}}

goのメインコードはこちら

main.go
package main

import (
   "html/template"
   "os"
   "time"
)

var tpl *template.Template

func init() {
   tpl = template.Must(template.ParseGlob("templates/*"))
}

func main() {

   type source struct {
   	Title, Message string
   	Time           time.Time
   }

   nf, _ := os.Create("index.html")
   defer nf.Close()

   var data source
   data = source{
   	Title:   "test page",
   	Message: "本日は晴天なり",
   	Time:    time.Now(),
   }
   _ = tpl.ExecuteTemplate(nf, "tpl.gohtml", data)
}

実行結果は、

goコードとテンプレートの使い分けですが、goコードではデータの作成や計算部分を担当し、テンプレートで表示形式を整えるのが分かりやすいと思います。テンプレートでインナー定義関数を利用して計算や大小比較することは可能ですが、ごちゃごちゃしてしまうことがよくあります。役割分担をスパッと切り分けたほうが管理上も精神衛生的にも良いでしょう。