見出し画像

【Go初学】template.ParseFiles() or Template.ParseFiles()

#概要

Golangでhtmlのビューファイルを表示する際に使う template.New()、template.ParseFiles()、*Template.ParseFilesについて気になって調べてみた点について記述したい。

#機能

▼template.New(name string)

New は、与えられた名前の新しい HTML テンプレートを割り当てます。

template.go

コメントの通り、引数で渡した名前でTemplate構造体を生成してポインタを返す。また内部では map[string]*Template の入れ子の形でテンプレートセットとなっており、複数のテンプレートを保持する構造となっている。
引数の文字列は map の key としても使用される。
これは *Template.Lookup(name) でテンプレート抽出する際に用いられるため、一意のものとする必要がある。

▼template.ParseFiles(filenames ...string)

ParseFilesは新しいTemplateを作成し、指定されたファイルからテンプレート定義をパースします。
返されるテンプレートの名前は、最初のファイルの(ベース)名と(パースされた)内容を持つことになります。

template.go

こちらは複数のテンプレートファイルのパスを渡し、テンプレートのポインターを返す。上で記載している通り複数のテンプレートを保持する構造のため、内部ではそれぞれのテンプレートを読み込み(os.ReadFile)、ファイルの中身を検証し、新規のテンプレートであればテンプレートセットへ追加している。

追加の際のテンプレート名(mapのkey)は、ファイルパスのディレクトリ識別を行う "/" 以降の文字列としているため、テンプレート名は任意の文字列というよりファイル名("index.html"など)を使うことを想定していると思われる。

ちなみに内部では*Template.Parse(text string) を呼び出しており、渡されたファイルの中身を検証と同時にテンプレートセットへの追加を行なっている。デバッグやサンプルのテスト時などでビューの中身(HTML構造文)を直接記述したものを扱いたい場合もこちらを複数回呼び出すことでテンプレートセットが取得できると思われる。

▼*Template.ParseFiles(filenames ...string)

ParseFilesは、新しいテンプレートを作成し、指定されたファイルからテンプレート定義を解析します。 返されるテンプレートの名前には、最初のファイルの(ベース)名と(解析された)内容が含まれます。

tenplate.go

こちらは上記2つと異なり、templateパッケージの関数であり、Template構造体を必要とせず呼び出せる。期待する結果は、内部で上記template.ParseFiles() と同じ関数を呼び出しているため、Template構造体経由の呼び出しかそうでないかの違いで結果は同じものになる。

#使い分け

上記の通り似たようなものがありどういう使い分けをするものなのかが気になっていたが、以下のケースで使い分けが必要になった。

▼テンプレートへ関数を渡して実行したい

テンプレートを描画する際に関数を渡して実行したいケースがある。例として何らかの時間を表す time.Time の表現をカスタマイズしたい場合、 *Template.Funcs() に template.FuncMap の形で関数を渡すことでテンプレート内で利用できるようになる。

// main.go
~
  t := template.New(viewName)
  t = t.Funcs(template.FuncMap{"TimeToSimple": TimeToSimple})
  t, _ = t.ParseFiles(layoutFilePath, viewName)
  t.ExecuteTemplate(w, layoutName, args)
}

func TimeToSimple(time time.Time) string {
  return fmt.Sprintf("%d年%d月%d日", time.Year(), time.Month(), time.Day())
}
// ビューファイル
<p>{{.HogeDate | TimeToSimple}}</p>

その際、関数を渡す前に直接 template.ParseFiles() でテンプレートのパースと生成を行おうとするとエラーになってしまう。こちらはパースの字句解析で {{ if }} など構文を検証する処理において、事前に関数(例では「TimeToSimple」)が渡されている必要がある。
そのためテンプレート内で関数を用いる場合は、先にテンプレート内関数を渡してからパースという手順に、用いない場合は template.ParseFiles() として複数の Template の生成とパースを同時に行うという手順で使い分けする。
ちなみに New() も Funcs() も *Template を返す関数/メソッドのため、メソッドチェーンで簡潔に記述することができる。

t, _ := template.New(viewName).
	Funcs(template.FuncMap{"TimeToSimple": TimeToSimple}).
	ParseFiles(layoutFilePath, viewFilePath)
t.ExecuteTemplate(w, layoutName, args)

#あとがき

今回 Funcs() の渡し方が正しくなく実行時エラーで悩んだため標準パッケージを覗いてみた。template.parseFiles() で引数*Templateが nil である前提の処理があったり、複数テンプレートを渡してどうやって1つのTemplateでまとめているのかなどなるほどなと勉強になった。
今回標準パッケージを深掘りしてみたが、スマートというよりは泥臭いコードに見え、これまで何となくGo言語に親しみを感じていたのはどことなくC言語を思い出させるところだったのかなと感じた。

この記事が気に入ったらサポートをしてみませんか?