LoginSignup
3
1

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-01-30

はじめに

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

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

やりたいこと

今回は、名前入力ページ名前出力ページを作成し、
2つのページの共通部分(ヘッダー&フッター)をGoの標準パッケージのhtml/templateを使い共通化します。
sample.gif

まずは基本から

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

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

続いて、index.html.gtplsigin.html.gtplを作成します。

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

上記の、2つのテンプレートでは、TitleNameFooterがあります。
次に、main.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)
    }
}

テンプレートを読み

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
}

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

値を詰める

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のファイルを作成します。

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}}
で定義します。

_header.html.gtpl
{{ define "header" }}
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>{{ .Title }}</title>
</head>
<body>
  <h1>{{ .Title }}</h1>
{{ end }}
_footer.html.gtpl
{{ define "footer" }}
  <p>&copy;{{ .Footer }}</p>
</body>
</html>
{{ end }}

部品が完成しました。

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

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" . }}
sigin.html.gtpl
{{ template "header" . }}
  <p>こんにちわ、{{ .Name }}さん。</p>
{{ template "footer" . }}

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

loadTemplateの編集

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

部品の値の構造体を定義

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

type common struct {
    Title  string
    Footer string
}

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

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

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からの値を含めた構造体を定義してください。

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

さいごに

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

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1