Golangでヘッダー、フッターをテンプレート化する

# はじめに
Goの標準パッケージの`html/template`を使い、テンプレート化できるところはテンプレート化していきます。

では、さっそくやっていきましょう！

# やりたいこと

今回は、**名前入力ページ**と**名前出力ページ**を作成し、
２つのページの共通部分(ヘッダー&フッター)をGoの標準パッケージの`html/template`を使い共通化します。
![sample.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/381808/67f67b4f-2583-d5da-836b-3732caf2826a.gif)

# まずは基本から

まず、下記のようにファイルを作成します。

``` shell
myApp
 ├── /client
 │      ├── index.html.gtpl
 │      └── sigin.html.gtpl
 └── main.go
```

続いて、`index.html.gtpl`と`sigin.html.gtpl`を作成します。

``` html:index.html.gtpl
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>{{ .Title }}</title>
</head>
<body>
  <h1>{{ .Title }}</h1>
  <form action="/sigin" method="POST">
    <input type="text" name="name">
    <button type="submit">送信</button>
  </form>
  <p>&copy;{{ .Footer }}</p>
</body>
</html>
```

``` html:sigin.html.gtpl
<!-- 省略 --->
  <title>{{ .Title }}</title>
</head>
<body>
  <h1>{{ .Title }}</h1>
  <p>こんにちわ、{{ .Name }}さん。</p>
  <p>&copy;{{ .Footer }}</p>
</body>
</html>
```

上記の、２つのテンプレートでは、`Title`、`Name`、`Footer`があります。
次に、`main.go`を作成します。

``` go:main.go
package main

import (
	"html/template"
	"log"
	"net/http"
)

var templates = make(map[string]*template.Template)

func init() {
	templates["index"] = loadTemplate("index")
	http.HandleFunc("/", index)
	templates["sigin"] = loadTemplate("sigin")
	http.HandleFunc("/sigin", sigin)
}

func index(w http.ResponseWriter, r *http.Request) {
	data := struct {
		Title  string
		Footer string
	}{
		Title:  "Go template Lesson",
		Footer: "2020 Go template Lesson",
	}
	if err := templates["index"].Execute(w, data); err != nil {
		log.Printf("failed to execute template: %v", err)
	}
}

func sigin(w http.ResponseWriter, r *http.Request) {
	data := struct {
		Title  string
		Name   string
		Footer string
	}{
		Title:  "Go template Lesson",
		Name:   r.FormValue("name"),
		Footer: "2020 Go template Lesson",
	}
	if err := templates["sigin"].Execute(w, data); err != nil {
		log.Printf("failed to execute template: %v", err)
	}
}

func loadTemplate(name string) *template.Template {
	t, err := template.ParseFiles("client/" + name + ".html.gtpl")
	if err != nil {
		log.Fatal("ParseFiles: ", err)
	}
	return t
}

func main() {
	if err := http.ListenAndServe(":8081", nil); err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
```

## テンプレートを読み

``` go
var templates = make(map[string]*template.Template)

func init() {
    templates["index"] = loadTemplate("index")
    http.HandleFunc("/", index)
    templates["sigin"] = loadTemplate("sigin")
    http.HandleFunc("/sigin", sigin)
}

func loadTemplate(name string) *template.Template {
    t, err := template.ParseFiles("client/" + name + ".html.gtpl")
    if err != nil {
        log.Fatal("ParseFiles: ", err)
    }
    return t
}
```

こちらは起動時にテンプレートファイルを読み込んでいます。

## 値を詰める

``` go
func index(w http.ResponseWriter, r *http.Request) {
    data := struct {
        Title  string
        Footer string
    }{
        Title:  "Go template Lesson",
        Footer: "2020 Go template Lesson",
    }
    if err := templates["index"].Execute(w, data); err != nil {
        log.Printf("failed to execute template: %v", err)
    }
}
```

あとは、パースしたテンプレートファイルに値を詰めて実行します。
今回は`struct`でやりましたが、`map`でも可能です。

それでは、`go run main.go`で実行し、サーバを起動してみましょう。

動きはできたと思います。

# ヘッダーとフッターの共通化

それでは、本題です。
ここからヘッダーとフッターのテンプレート定義し作成します。

まずは、`_header.html.gtpl`と`_footer.html.gtpl`のファイルを作成します。

``` shell
myApp
 ├── /client
 │ 　　　├── /template
 │      │       ├── _header.html.gtpl
 │      │       └── _footer.html.gtpl
 │      ├── index.html.gtpl
 │      └── sigin.html.gtpl
 └── main.go
```

続いて`_header.html.gtpl`と`_footer.html.gtpl`に部品を定義します。
部品を定義するときは、
`{{define "NAME"}}`〜要素〜`{{end}}`
で定義します。

``` html:_header.html.gtpl
{{ define "header" }}
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>{{ .Title }}</title>
</head>
<body>
  <h1>{{ .Title }}</h1>
{{ end }}
```

``` html:_footer.html.gtpl
{{ define "footer" }}
  <p>&copy;{{ .Footer }}</p>
</body>
</html>
{{ end }}
```

部品が完成しました。

最後に`index.html.gtpl`と`sigin.html.gtpl`を編集します。
部品を呼び出す際は、
`{{template "NAME" }}` or `{{template "NAME" .}}`
で呼び出します。
`.` があるかないかは、値を渡しているかで変わります。
値を渡さない静的なテンプレートの場合は、`{{template "NAME" }}`
値を渡す動的なテンプレートの場合は、`{{template "NAME" .}}`
で呼び出します。

``` html:index.html.gtpl
{{ template "header" . }}
  <main>
    <form action="/sigin" method="POST">
      <input type="text" name="name" tabindex="1">
      <button type="submit" tabindex="0">送信</button>
    </form>
  </main>
{{ template "footer" . }}
```

``` html:sigin.html.gtpl
{{ template "header" . }}
  <p>こんにちわ、{{ .Name }}さん。</p>
{{ template "footer" . }}
```

それでは最後に`main.go`を編集します。

## loadTemplateの編集

``` go
func loadTemplate(name string) *template.Template {
	t, err := template.ParseFiles(
		"client/"+name+".html.gtpl",
		"./client/template/_header.html.gtpl",
		"./client/template/_footer.html.gtpl",
	)
	if err != nil {
		log.Fatal("template ParseFiles: ", err)
	}
	return t
}
```

`ParseFiles()` の引数は、可変長になっていて複数のパスを受け取ります。 
最上位となるテンプレートを第一引数に渡したあと、その後ろに部品を列挙していきます。
なので部品となるテンプレートは第二引数以降に記述してください！

## 部品の値の構造体を定義

部品となるテンプレートの値となる構造体を作成します。

``` go
type common struct {
	Title  string
	Footer string
}

var tmpData = common{
	Title:  "Go template Lesson",
	Footer: "2020 Go template Lesson",
}
```

さいごに各ページの関数に`tmpData`を渡してあげます。

``` go
func index(w http.ResponseWriter, r *http.Request) {
	if err := templates["index"].Execute(w, tmpData); err != nil {
		log.Printf("failed to execute template: %v", err)
	}
}

func sigin(w http.ResponseWriter, r *http.Request) {
	data := struct {
		common
		Name string
	}{
		common: tmpData,
		Name:   r.FormValue("name"),
	}
	if err := templates["sigin"].Execute(w, data); err != nil {
		log.Printf("failed to execute template: %v", err)
	}
}
```

`index()`は定義した構造体をそのまま使えば問題ないですが、`sigin()`では、構造体を継承し、formからの値を含めた構造体を定義してください。

これで再度、実行してみてください。

# さいごに

今回は、共通化できる部品をテンプレート化してみました。
ある程度の規模なら楽に開発できそうですね。