April 1, 2012

1722 letters 4 mins read

Go言語:templateパッケージでHTML等テキストファイルを読み込む(応用編1)

templateパッケージでHTML等の任意のテキストファイルを読み込み、出力する方法を説明する。 応用編ではテンプレート内に埋め込む「アクション」について解説する。 なお、APIバージョンGo1で説明する。 バージョン3ではパッケージ名、関数名が異なるので注意。

概要

templateパッケージでは、任意のテキストを読み込み、テキストに埋め込まれたタグに対してデータの評価や制御(アクションという)をおこなって出力する。いくつかのアクションの指定により、以下のようなことができる。

  • テンプレートに渡された引数や構造体のフィールドを出力する。
  • テンプレートに渡されたbool値を評価し、出力を制御する。
  • テンプレートに渡された配列、Slice、Mapのデータを繰り返し出力する。

配列の要素をすべて表示するサンプル

package main

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

const templateString string = `テンプレートの始め
{{range .}}
繰り返し処理 {{.}}
{{end}}
テンプレートの終わり
`

func main() {
	var stringArray []string = []string{"Hello", "World", "!!!!"}
	var t = template.Must(template.New("html").Parse(templateString))

	if err := t.Execute(os.Stdout, stringArray); err != nil { // 配列を渡す 
		fmt.Println(err.Error())
	}
}

出力結果

テンプレートの始め

繰り返し処理 Hello

繰り返し処理 World

繰り返し処理 !!!!

テンプレートの終わり

構造体、Mapの要素の表示、if文による条件分岐を用いたサンプル

package main

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

type Struct struct {
    SampleString string
    SampleMap map[string]MapValue
    SampleBool bool
}

type MapValue struct {
    IntValue int
    StringValue string
}

const templateString string = `テンプレートの始め
{{with .}}
    Structが空でなければこの先を出力する
    {{.SampleString}}:フィールドの値を出力
    {{range .SampleMap}}
        Mapの繰り返し処理 {{.IntValue}}: {{.StringValue}}
    {{end}}
    {{if .SampleBool}}
        bool変数がtrueならば出力
    {{else}}
        bool変数がfalseならば出力
    {{end}}
{{end}}
テンプレートの終わり
`

func main() {
    values := Struct{"サンプルテキスト", make(map[string]MapValue), false}
    values.SampleMap["first"] = MapValue{1, "Hello"}
    values.SampleMap["second"] = MapValue{2, "World"}
    values.SampleMap["third"] = MapValue{3, "!!!!"}
	var t = template.Must(template.New("text").Parse(templateString))

	if err := t.Execute(os.Stdout, values); err != nil { // 構造体を渡す 
		fmt.Println(err.Error())
	}
}

出力結果

テンプレートの始め

    Structが空でなければこの先を出力する
    サンプルテキスト:フィールドの値を出力
    
        Mapの繰り返し処理 1: Hello
    
        Mapの繰り返し処理 2: World
    
        Mapの繰り返し処理 3: !!!!
    
    
        bool変数がfalseならば出力
    

テンプレートの終わり

解説

アクションタグの種類

{{.}} テンプレートに渡された引数を出力する。

{{.Member}} テンプレートに渡された引数が構造体の場合、Memberフィールドの値を出力する。

{{if .}} 分岐1 {{else}} 分岐2 {{end}} テンプレートに渡された引数のbool値がtrueの場合「分岐1」が出力され、falseの場合「分岐2」が出力される。{{else}}は省略できる。

{{range .}} {{.}} {{else}} なし {{end}} テンプレートに渡された引数が配列、Slice、Mapの場合、lengthが1以上の場合はすべての要素を表示し({{.}}を要素数分繰り返し)、lengthが0の場合には「なし」が表示される。{{else}}は省略できる。

{{with .}} 分岐1 {{else}} 分岐2 {{end}} テンプレートに渡された引数の値が空でない場合「分岐1」が出力され、空の場合「分岐2」が出力される。{{else}}は省略できる。

応用

テンプレートを実際のWebアプリケーションに用いる例を紹介する。 サンプルとしてLegacy-BBSを参照してもらいたい。 このサンプルは、下記の2つの部分に分かれている。

  • メッセージを投稿するためのフォーム部分 投稿時に名前やメッセージが空白でないかのバリデーションを行い、エラーメッセージを表示する。 バリデーションの結果を{{if}}で判定し、エラーの表示をおこなう。
  • 投稿されたメッセージを表示する部分 データストアに格納されている複数のメッセージを表示する。 メッセージは構造体Entry_viewの配列に格納し、{{range}}により表示をおこなう。

構造体とテンプレートのサンプルは下記の通り。 なお、この例にはこれらの構造体のデータを取得・出力するロジックは省略している。 ロジックについてはまた別の機会に説明する。

type View struct {
	Entries      []Entry_view
	Errors       Validation
}

type Entry_view struct {
	Name    string
	Email   string
	Title   string
	Message string
	Date    string
}

type Validation struct {
	NameError    bool
	MessageError bool
}
<html>
<body>
    <p>Legacy BBS Sample</p>
    {{/* メッセージを投稿するためのフォーム部分 */}}
    <form method="POST" action="">
        {{with .Errors}}
            Submitter
            <input type="text" name="name" /><br />
            {{if .NameError}}<font color="red">Enter a submitter</font><br />{{end}}
            Mail
            <input type="text" name="email" /><br />
            Title
            <input type="text" name="title" /><br />
            Message
            <textarea name="message"></textarea><br />
            {{if .MessageError}}<font color="red">Enter a message</font><br />{{end}}
            <input type="submit" name="submit" value="Submit" />
        {{end}}
    </form>

    {{/* 投稿されたメッセージを表示する部分 */}}
    {{range .Entries}}
        <hr />
        <p>
            {{.Title}}  Submitter:
            {{if .Email}}<a href="mailto:{{.Email}}">{{.Name}}</a>
            {{else}}{{.Name}}
            {{end}}
            Date: {{.Date}}
        </p>
        <p>{{.Message}}</p>
    {{end}}
</body>
</html>

応用編2ではアクションタグに使用できる関数や変数を説明する。