Go言語でタイトルなどを動的に埋め込んでHTMLを出力するテンプレートの書き方

HTML ファイルを作成する時、記事内容を別ファイルとして保存し切り離しておくと、ベースとなる1つのHTMLファイルを更新するだけで全てのページの構造を変更することが可能ですよね。

また、記事のタイトル、公開日、更新日などを動的に挿入したい時がありますが、Go言語ではHTMLのテンプレートパッケージを使うことで簡単に実現が可能です。

まずは、シンプルなコードをご覧ください。

コンテンツの転載は固くお断りいたします。

HTMLにタイトルを埋め込むだけのシンプルなコード

1つ目はGoソースファイルです。以下のコードの目的は『title』タグの中に『Test』という文字列を埋め込むことです。


package main

import (
	"bytes"
	"fmt"
	"html/template"
	"io"
	"log"
)

func main() {
	tmpl := template.Must(template.ParseFiles("base.html"))
	buff := new(bytes.Buffer)
	fw := io.Writer(buff)
	dat := struct {
		Title string
	}{
		Title: "Test",
	}
	if err := tmpl.ExecuteTemplate(fw, "base", dat); err != nil {
		log.Fatal(err)
	}

	fmt.Println(string(buff.Bytes()))
}

2つ目はHTMLテンプレートファイルです。拡張子はhtmlで構いません。ファイル名は『base.html』です。


出力結果

出力結果は以下のようになり、titleタグ部分に『Test』という文字列が埋め込まれています。


Goソースファイルの解説

では、どのようにしてテンプレートファイルからHTMLファイルを生成したのかを解説していきます。

必要なパッケージ


import (
	"bytes"
	"fmt"
	"html/template"
	"io"
)

『bytes』パッケージはバイトデータのバッファーなどを用意します。『io』パッケージは、バッファーにデータを書き込むのに使用しています。『html/template』パッケージはその名の通り、HTMLテンプレートを扱うためのものです。

テンプレートファイルのパスを指定する


tmpl := template.Must(template.ParseFiles("base.html"))

『ParseFiles』関数に使用するテンプレートファイルのアドレスを指定しています。この関数を使用し、上記のように書いておくと複数のテンプレートファイルから1つのHTMlファイルの生成も可能になります。

おそらく、ほとんどのケースでHTMLファイルが複雑化してくると思うので、その場合はいくつかのテンプレートファイルに分けておくとコードが見やすくなり便利ですよね。

なので初めから分けておき、上記のようなコードを実装しておくと良いかと思います。

出力されたHTMLを格納しておくバッファーと、書き込むためのライターを用意する


buff := new(bytes.Buffer)
fw := io.Writer(buff)

『bytes.Buffer』を『new』で新規作成しています。そして『buff』を『io.Writer』に渡して『fw』を用意しています。この『fw』というライターを使って出力されたHTMLデータを『buff』に書き込みます。

テンプレートファイルに埋め込むデータを指定する


dat := struct {
    Title string
}{
    Title: "Test",
}

『dat』という構造体を定義し、指定したデータで初期化しています。今回は文字列型の『Title』というもの用意し、『Test』という文字列を渡しています。

なお、構造体のデータ名は頭文字を大文字にしておいてください。小文字にするとテンプレートファイルに埋め込むことが出来ません。

テンプレートファイルに指定したデータを埋め込み、HTMLを出力する


if err := tmpl.ExecuteTemplate(fw, "base", dat); err != nil {
    log.Fatal(err)
}

fmt.Println(string(buff.Bytes()))

『ExecuteTemplate』関数を使用してテンプレートファイルを処理し、HTMLデータを出力しています。

1つ目の引数にはライターを指定し、2つ目にはテンプレート名を指定します。そして3つ目には埋め込むデータの構造体を指定します。テンプレート名というのは後ほど解説します。

そして、エラーが無ければデータを表示しています。

テンプレートファイルの解説

では次に、テンプレートファイルの内容を解説していきます。

テンプレート名を定義する


1行目でテンプレート名を定義しています。テンプレートファイル特有のデータ等は全て『{{』と『}}』で囲っておきます。そうすることでテンプレートに関係する記述であると認識されます。

テンプレート名とは他のテンプレートファイルと区別するために使用します。今回のファイルは『base』という名前を付けていますが、このテンプレートファイルに別のテンプレートを埋め込みたい時、区別していないと大変ですよね。

『{{define』と記述したら、テンプレートファイルの最後に『{{end}}』と記述し、ここまでが『base』というテンプレートであることを明記しておいてください。

タイトルデータを埋め込む場所を指定する


上記のコードはとてもシンプルなHTMLファイルの書き方です。今回は『title』タグの部分に注目してみてください。『{{.Title}}』という記述がありますよね。

Go言語で定義した構造体データなどにアクセスする際は、データ名の最初に『.』を付けてアクセスします。これで、この場所にタイトルが埋め込まれるようになります。

とても簡単ですし、シンプルですよね。

テンプレートファイルに別のテンプレートファイルを埋め込みたい時のコード

Goコードの一部を以下のように変更しておきます。ParseFiles の部分に『"body.html"』を追加し、構造体部分に文字列型の『Name』を追加し『Yufi』というデータを渡しておきます。


tmpl := template.Must(template.ParseFiles("base.html", "body.html"))

dat := struct {
	Title string
	Name  string
}{
	Title: "Test",
	Name:  "Yufi",
}

base.html は以下の『body』部分を追加しています。別のテンプレートファイルを埋め込みたい場合は『{{template "body" .}}』と記述し、今回は『body』というテンプレートを埋め込みます。


そして『body.html』というファイルを新しく作成しておきます。テンプレート名は『body』と定義し、『Hello {{.Name}}』を表示させるシンプルなものです。

構造体で新しく追加した『Name』のデータを表示させています。


出力結果

出力結果は以下のようになり、Name に指定した文字列が埋め込まれ、『body』テンプレートも埋め込まれ1つのHTMLデータとして出力されているのが確認できます。


その他のテンプレートの記述例

テンプレートファイルでは、データを埋め込む以外にテンプレートファイル内で条件分岐や、ループなどを行うことが可能です。それらの使い方をご紹介していきます。

条件分岐

Goコードの構造体は以下のようにしておきます。bool型を定義し、朝であればtrue、そうで無ければfalseです。


dat := struct {
	Morning bool
}{
	Morning: true,
}

テンプレートは以下のように記述します。『if』の後に評価するデータ名を指定します。『else』も指定が可能であり、最後は『end』で閉じておきます。


ループ処理

Goコードは以下のように、文字列のスライスを用意します。


list := []string{"おはよう", "こんにちは", "こんばんは"}

dat := struct {
	List []string
}{
	List: list,
}

テンプレートは以下のようにし、『range』と記述してスライス名を指定しています。最後は『end』で閉じておいてください。スライスのデータにアクセスする際は『{{.}}』と記述します。


出力結果は以下のようになり、スライスのデータが順に表示されているのを確認できますね。


構造体へのアクセス

テンプレートファイルに構造体データを渡したい時もありますよね。その場合は以下のようになります。

Goコードには『People』という構造体を用意し、文字列の『Name』と数値の『Age』を含みます。そして、それをスライスとしてリスト化します。


type People struct {
	Name string
	Age  int
}

list := []People{
	{"太郎", 18},
	{"次郎", 15},
	{"花子", 12},
}

dat := struct {
	List []People
}{
	List: list,
}

テンプレートファイルは以下のようになり、『range』で『List』のデータの数だけループさせ順番に取得し、『.Name』と『.Age』でそれぞれ名前と年齢を表示しています。


出力結果を見て分かる通り、数値も問題なく表示されていますね。


ちなみにですが、テンプレートの内容を以下のようにすると…


結果は次のようになります。『fmt.Print』関数に構造体をそのまま引数として渡した時の同じ結果になります。


終わりに

いかがでしたか?

『{{ }}』と囲むだけで必要なデータを埋め込むことが出来て、Goコードもとてもシンプルですよね。データ名をそのまま使用できるのは便利ですし、条件分岐やループ処理も簡単です。

詳しくは『GoDoc の html/template パッケージページ』をご覧ください。