1. rubytomato@github

    Posted

    rubytomato@github
Changes in title
+Revel templatesを使ったサンプルアプリケーション
Changes in tags
+Go
1.4.2
+Revel
0.12.0
Changes in body
Source | HTML | Preview
@@ -0,0 +1,2010 @@
+# 概要
+
+Revel frameworkのtemplatesとGolangのhtml/templateパッケージの機能を使ったwebアプリケーションのサンプルコードです。
+サンプルコードを題材にテンプレートの機能の使い方を説明していきます。そのため複雑な処理は行わずにコントローラー側でデータを準備しそれをテンプレート側で出力する簡単なアプリケーションになります。
+
+**環境**
+
+* Windows7 (64bit)
+* The Go Programming Language 1.4.2
+* Revel 0.12.0
+
+**参考サイト**
+
+Golang
+
+* [Golang - Package text/template] (http://golang.org/pkg/text/template/)
+* [Golang - Package text/template 日本語] (http://golang-jp.org/pkg/text/template/)
+* [Golang - Package html/template] (http://golang.org/pkg/html/template/)
+
+Revel
+
+* [Revel manual - Templates] (https://revel.github.io/manual/templates.html)
+
+etc
+
+* [GoLang Tutorials - Go Templates] (http://golangtutorials.blogspot.jp/2011/06/go-templates.html)
+* [andlabs's blog - THE GO TEMPLATES POST] (http://andlabs.lostsig.com/blog/2014/05/26/8/the-go-templates-post)
+
+
+## アプリケーションの雛形作成
+
+アプリケーション名を`tsp` (template sample)としました。
+newコマンドで雛形を生成しapp.goとIndex.html、_gw.htmlにサンプルコードを追加していきます。
+
+```bat:new
+> revel new tsp
+```
+
+```text
+tsp (アプリケーションROOT)
+ |
+ +--- /app
+ | |
+ | +--- /controllers
+ | | |
+ | | +--- app.go (サンプルコード)
+ |
+ +--- /views
+ |
+ +--- /App
+ |
+ +--- _gw.html (追加したテンプレート)
+ +--- Index.html (サンプルコード)
+```
+
+**完成図**
+
+このような感じのページが出力されます。
+
+![image01.png](https://qiita-image-store.s3.amazonaws.com/0/22772/0b3c4c7d-8238-a4ca-969a-8a984aa50221.png)
+
+![image02.png](https://qiita-image-store.s3.amazonaws.com/0/22772/fe758050-9dbf-e82c-f576-836a14351b01.png)
+
+![image03.png](https://qiita-image-store.s3.amazonaws.com/0/22772/67bded82-7257-d983-4093-f7d7e051cc94.png)
+
+## 使用するサンプルデータの定義
+
+**GameWatch**
+
+携帯ゲーム機の情報
+
+|field |name |
+|:-------------|----------------------------|
+|No |一意の番号 |
+|ModelNumber |型番 |
+|Title |タイトル.(ja:日本語、en:英語) |
+|ReleaseDate |発売日 |
+|Price |定価 |
+|Series |シリーズ名 |
+|Million |ミリオンヒットフラグ (true:100万本以上販売) |
+|Premium |プレミア度 (S,A,B,Cの4段階) |
+|VariousColor |カラーバリエーション |
+|Genre |ジャンル |
+|Character |操作キャラクター |
+
+**Series**
+
+携帯ゲーム機のシリーズの情報
+
+|field |name |
+|:-------------|-----------------|
+|Generation |世代 |
+|Name |シリーズ名 |
+|NumOfTitles |発売タイトル数 |
+|Feature |特徴 |
+|Battery |使用する電池の種類 |
+
+**Battery**
+
+使用するボタン電池の情報
+
+|field |name |
+|:-------------|-----------------|
+|Code |記号 |
+|Diameter |直径 |
+|Height |厚み |
+|Voltage |電圧 |
+
+
+## controllerでサンプルデータの準備
+
+controller側でサンプルデータの準備を行い、それをテンプレートへ渡します。
+下記はapp.goの一部を抜粋したコードです。(全コードは記事のおわりに掲載しています)
+
+```go:app.go(抜粋)
+package controllers
+
+import (
+ "fmt"
+ "html/template"
+ "strings"
+ "time"
+
+ "github.com/dustin/go-humanize"
+ "github.com/revel/revel"
+)
+
+type App struct {
+ *revel.Controller
+}
+
+/*
+GameWatch は携帯ゲーム機の情報を持つ
+*/
+type GameWatch struct {
+ No int
+ ModelNumber string
+ Title map[string]string
+ ReleaseDate time.Time
+ Price int
+ *Series
+ Million bool
+ Premium string
+ VariousColor []string
+ Genre string
+ Character string
+}
+
+/*
+Series は携帯ゲーム機のシリーズの情報を持つ
+*/
+type Series struct {
+ Generation int
+ Name string
+ NumOfTitles int
+ Feature string
+ Battery []*ButtonCell
+}
+
+func (s Series) Description() template.HTML {
+ return template.HTML(fmt.Sprintf("第%d世代 名称:「%s」 発売タイトル:%d本 特徴:「%s」", s.Generation, s.Name, s.NumOfTitles, s.Feature))
+}
+
+/*
+ButtonCell は携帯ゲーム機で使用できるボタン電池の情報を持つ
+*/
+type ButtonCell struct {
+ Code string
+ Diameter float32
+ Height float32
+ Voltage float32
+}
+
+func (c App) Index() revel.Result {
+
+ seriesListTitle := "携帯ゲーム機シリーズ一覧"
+ gameWatchListTitle := "携帯ゲーム機一覧"
+
+ /* シリーズのスライス */
+ seriesSlice := []*Series{
+ silver,
+ gold,
+ wide,
+ multi,
+ panorama,
+ newwide,
+ super,
+ }
+
+ /* シリーズのマップ */
+ seriesMap := map[string]*Series{
+ "silver": silver,
+ "gold": gold,
+ "wide": wide,
+ "multi": multi,
+ "panorama": panorama,
+ "super": super,
+ }
+
+ /* ゲーム機のスライス */
+ gamewatchSlice := []*GameWatch{
+ ac01,
+ fl02,
+ mt03,
+ rc04,
+ ip05,
+ mh06,
+ cn07,
+ ln08,
+ pr21,
+ oc22,
+ dk52,
+ dj101,
+ sm91,
+ bu201,
+ }
+
+ labelMap := map[string]interface{}{
+ "title": "Home",
+ "description": "Revel Framework template sample",
+ }
+
+ return c.Render(gamewatchSlice, seriesSlice, seriesMap, gameWatchListTitle, seriesListTitle, labelMap)
+}
+
+func CurrencyFormat(price int) string {
+ str := humanize.Comma(int64(price))
+ return str + "円"
+}
+
+var (
+ lr43, sr43, lr44, sr44 *ButtonCell
+ silver, gold, wide, multi, panorama, newwide, super *Series
+ ac01, fl02, mt03, rc04, ip05, mh06, cn07, ln08, pr21, oc22, dk52, dj101, sm91, bu201 *GameWatch
+)
+
+func init() {
+ fmt.Printf("sample.go init()\n")
+
+ //独自関数の登録
+ revel.TemplateFuncs["CurrencyFormat"] = CurrencyFormat
+ revel.TemplateFuncs["ToLower"] = strings.ToLower
+
+ //電池の準備
+ lr43 = &ButtonCell{
+ Code: "LR43",
+ Diameter: 11.6,
+ Height: 4.2,
+ Voltage: 1.5}
+
+~省略~
+
+ //シリーズの準備
+ silver = &Series{
+ Generation: 1,
+ Name: "シルバー",
+ NumOfTitles: 5,
+ Feature: `本体前面のプレートが<b>シルバー</b><br/>最初期のシリーズ`,
+ Battery: []*ButtonCell{lr43, sr43}}
+ gold = &Series{
+ Generation: 2,
+ Name: "ゴールド",
+ NumOfTitles: 3,
+ Feature: `本体前面のプレートが<b>ゴールド</b><br/>アラーム機能、背面にスタンド付き`,
+ Battery: []*ButtonCell{lr43, sr43}}
+
+~省略~
+
+ lc, _ := time.LoadLocation("Asia/Tokyo")
+
+ //携帯ゲーム機の準備
+ ac01 = &GameWatch{
+ No: 1,
+ ModelNumber: "AC-01",
+ Title: map[string]string{"ja": "ボール", "en": "BALL"},
+ ReleaseDate: time.Date(1980, 4, 28, 0, 0, 0, 0, lc),
+ Price: 5800,
+ Series: silver,
+ Million: false,
+ Premium: "A",
+ VariousColor: []string{"RED"},
+ Genre: "お手玉",
+ Character: "ロボット",
+ }
+
+ fl02 = &GameWatch{
+ No: 2,
+ ModelNumber: "FL-02",
+ Title: map[string]string{"ja": "フラッグマン", "en": "FLAGMAN"},
+ ReleaseDate: time.Date(1980, 6, 05, 0, 0, 0, 0, lc),
+ Price: 5800,
+ Series: silver,
+ Million: false,
+ Premium: "S",
+ VariousColor: []string{"ORANGE"},
+ Genre: "数字当て",
+ Character: "フラッグマン",
+ }
+
+~省略~
+
+}
+```
+
+## templateのサンプルコードの説明
+
+コントローラー側から渡されたデータを使ってテンプレートを処理します。
+
+**テンプレートで使用するデータ**
+
+|name |data type |
+|:------------------|:----------------------|
+|seriesListTitle |string |
+|gameWatchListTitle |string |
+|seriesSlice |[]*Series{} |
+|seriesMap |map[string]*Series{} |
+|gamewatchSlice |[]*GameWatch{} |
+|labelMap |map[string]interface{} |
+
+### `.`(ドット)について
+
+`.`(ドット)はテンプレートからデータ構造内の要素を参照するときに使用するカーソルです。
+Revel templatesを使うとデータ構造はController型に定義されているRenderArgsというmapになります。
+controllerのRenderメソッドに渡す引数はこのmapへ追加されテンプレートから参照できるようにます。
+
+> ```go:Controller(抜粋)
+> type Controller struct {
+>
+> RenderArgs map[string]interface{} // Args passed to the template.
+>
+> }
+> ```
+
+> ```go:Render(抜粋)
+> func (c *Controller) Render(extraRenderArgs ...interface{}) Result
+> ```
+>
+> Render a template corresponding to the calling Controller method. Arguments will be added to c.RenderArgs prior to rendering the template. They are keyed on their local identifier.
+
+#### set関数
+
+setはRevelのグローバル関数です。
+テンプレート内からデータ構造に新しいデータを追加することができます。
+第1引数に指定するデータ構造に、第2引数で指定する名前に第3引数で指定する値を登録します。
+
+このサンプルでは`.`(RenderArgs)に"title"と"description"という要素を追加します。
+
+```html:index.html
+{{set . "title" "Home"}}
+{{set . "description" "Revel Framework template sample"}}
+{{template "header.html" .}}
+```
+
+```html:header.html
+<title>{{.title}}</title>
+```
+
+RenderArgsに無闇に要素を追加したくないといったニーズがあると思いますが、そのときは別途`map[string]interface{}`型の要素を定義することで対応できます。
+このサンプルは`labelMap`にテンプレート内から新しい要素を追加します。
+
+```go:app.go_labelMap(抜粋)
+labelMap := map[string]interface{}{
+ "title": "Home",
+ "description": "Revel Framework template sample",
+}
+
+c.Render(gamewatchSlice, seriesSlice, seriesMap, gameWatchListTitle, seriesListTitle, labelMap)
+```
+
+```html:index.html
+{{set .labelMap "keyword" "set function example"}}
+```
+
+#### append関数
+
+appendはRevelのグローバル関数です。
+テンプレート内からデータ構造に新しいデータを追加することができます。
+第1引数に指定するデータ構造に、第2引数で指定する名前の配列へ第3引数で指定する値を追加します。
+
+これは雛形のheader.htmlにあるページ固有のcssファイルを読み込むサンプルです。
+ページごとに異なるcssでレイアウトを変えたい場合などの用途で使用できます。
+
+```html:index.html
+{{append . "moreStyles" "css/index.custom.css"}}
+```
+
+```html:header.html
+{{range .moreStyles}}
+ <link rel="stylesheet" type="text/css" href="/public/{{.}}">
+{{end}}
+```
+
+**debug.html**
+
+雛形に含まれるdebug.htmlテンプレートでは`.`が持つ要素をすべて表示するコードがあります。
+
+```html:debug.html
+<h4>Available pipelines</h4>
+<dl>
+{{ range $index, $value := .}}
+ <dt>{{$index}}</dt>
+ <dd>{{$value}}</dd>
+{{end}}
+</dl>
+```
+
+Revel frameworkによってRenderArgsには次の名前を持つ要素が追加されます。
+
+* errors
+* flash
+* DevMode
+* RunMode
+* currentLocale
+
+アプリケーションをデバッグモードで実行するとdebug.htmlテンプレートが組み込まれるので下図のようなデバッグ情報を確認することができます。
+
+![debug_mode.png](https://qiita-image-store.s3.amazonaws.com/0/22772/a2691c28-7069-d0ec-aea1-e641311532ca.png)
+
+### rangeを使ったループ処理
+
+rangeアクション(Golang)で配列、マップ、スライスなどをループ処理することができます。
+
+このサンプルはseriesSliceをループ処理してテーブルとして出力します。
+rangeアクションのブロック内で`.`が指すデータ構造はseriesSliceの個々の要素になります。
+ネストする内側のrangeのブロックと外側のrangeのブロックでは`.`が指すデータ構造は切り替わります。
+
+```html:index.html_sample1
+{{/* sample 1 range */}}
+<div class="container">
+ <div class="row">
+ <div class="span12">
+ <table class="table table-bordered table-striped">
+ <caption>{{.seriesListTitle}} | sample 1</caption>
+ <thead>
+ <tr>
+ <th>idx</th>
+ <th>世代</th>
+ <th>名称</th>
+ <th>タイトル数</th>
+ <th>電池</th>
+ <th>特徴</th>
+ </tr>
+ </thead>
+ <tbody>
+ {{/* rangeブロック内ではseriesSliceの各要素をドット(.)で参照する */}}
+ {{range .seriesSlice}}
+ <tr>
+ <td></td>
+ <td>{{.Generation}}</td>
+ <td>{{.Name}}</td>
+ <td>{{.NumOfTitles}}</td>
+ <td>
+ {{if .Battery}}
+ <ul>
+ {{/* ネストしたrange */}}
+ {{range .Battery}}
+ <li>{{pad .Code 10}}({{.Voltage}}v)</li>
+ {{end}}
+ </ul>
+ {{end}}
+ </td>
+ <td>
+ {{/* raw関数でエスケープ処理を回避 */}}
+ {{raw .Feature}}
+ </td>
+ </tr>
+ {{end}}
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+```
+
+rangeアクションで変数を使用すると`.`の代わりにその変数をカーソルとして使用できます。
+このサンプルはループ処理の部分を抜粋したものですが、出力結果は上記のsample1とおなじになります。
+
+```html:index.html_sample2(抜粋)
+<tbody>
+{{/* ドット(.)の代わりに変数名($value)で参照する */}}
+{{range $index, $value := .seriesSlice}}
+ <tr class="{{if even $index}}info{{else}}{{end}}">
+ <td>{{$index}}</td>
+ <td>{{$value.Generation}}</td>
+ <td>{{$value.Name}}</td>
+ <td>{{$value.NumOfTitles}}</td>
+ <td>
+ {{if .Battery}}
+ {{/* ネストしたrange */}}
+ <ul>
+ {{range $cell := .Battery}}
+ <li>{{pad $cell.Code 10}}({{$cell.Voltage}}v)</li>
+ {{end}}
+ </ul>
+ {{end}}
+ </td>
+ <td>
+ {{/* raw関数でエスケープ処理を回避 */}}
+ {{raw $value.Feature}}
+ </td>
+ </tr>
+{{end}}
+</tbody>
+```
+
+#### pad関数
+
+padはRevelのグローバル関数です。
+第1引数に指定した文字列が第2引数で指定した数値の長さになるまで`&nbsp;`を付加します。
+
+```html:pad
+<li>{{pad .Code 10}}({{.Voltage}}v)</li>
+```
+
+#### raw関数
+
+rawはRevelのグローバル関数です。
+第1引数に指定した値をhtmlエスケープせずに出力します。
+
+```html:raw
+<td>
+ {{/* raw関数でエスケープ処理を回避 */}}
+ {{raw .Feature}}
+</td>
+```
+
+#### even関数
+
+evenはRevelのグローバル関数です。
+第1引数に指定した数値が偶数の場合はtrue、奇数の場合はfalseを返します。
+
+```html:even
+{{range $index, $value := .seriesSlice}}
+ {{/* $indexとeventを使って隔行ごとにclassを変える */}}
+ <tr class="{{if even $index}}info{{else}}{{end}}">
+ ...
+ </tr>
+{{end}}
+```
+
+### indexを使ったデータの操作
+
+index関数(Golang)で配列、マップ、スライスから添え字で指定した位置の要素を取り出すことができます。
+
+これはseriesSlice[1]の要素を変数$saにセットし、$saを使ってデータを参照するサンプルです。
+添え字に$idx変数を使用していますが、リテラルも使用できます。
+
+```html:index.html_sample3
+{{/* sample 3 index */}}
+<div class="container">
+ <div class="row">
+ <div class="span12">
+ {{/* indexで添え字を指定して特定要素を参照できる */}}
+ {{$idx := 1}}
+ {{$sa := index .seriesSlice $idx}}
+ <table class="table table-bordered table-striped">
+ <caption>ゴールド | sample 3</caption>
+ <tr>
+ <th>世代</th>
+ <td>{{$sa.Generation}}</td>
+ </tr>
+ <tr>
+ <th>名称</th>
+ <td>{{$sa.Name}}</td>
+ </tr>
+ <tr>
+ <th>タイトル数</th>
+ <td>{{$sa.NumOfTitles}}</td>
+ </tr>
+ <tr>
+ <th>特徴</th>
+ <td>{{raw $sa.Feature}}</td>
+ </tr>
+ <tr>
+ <th>備考</th>
+ <td>{{$sa.Description}}</td>
+ </tr>
+ </table>
+ </div>
+ </div>
+</div>
+```
+
+### ifを使った制御
+
+ifアクション(Golang)を使って条件分岐を行うことができます。ifブロック内の`.`には影響はありません。
+また、ge関数やlt関数などと組み合わせることができます。
+
+```html:sample5
+<td>
+ {{/* 比較演算子 */}}
+ {{if ge .Price 6000}}
+ <span style="color:red">*</span>{{.Price}}
+ {{else if lt .Price 5000}}
+ {{.Price}}
+ {{else}}
+ <span style="color:blue">*</span>{{.Price}}
+ {{end}}
+</td>
+```
+
+### withを使った制御
+
+withアクション(Golang)は、ifアクション(Golang)とよく似た働きをしますが`.`に影響を与える点が異なります。
+このサンプルでは`.`はwithアクションのブロック外ではRenderArgsを指しますが、ブロック内に制御が移るとseriesMap.multiを指します。
+
+```html:sample4
+{{/* sample 4 with */}}
+<div class="container">
+ <div class="row">
+ <div class="span12">
+ {{/*
+ mapのキーで参照できる
+ withはifと似ているがドットに影響を与える
+ */}}
+ {{with .seriesMap.multi}}
+ <table class="table table-bordered table-striped">
+ <caption>sample4 マルチスクリーン</caption>
+ <tr>
+ <th>世代</th>
+ <td>{{.Generation}}</td>
+ </tr>
+ <tr>
+ <th>名称</th>
+ <td>{{.Name}}</td>
+ </tr>
+ <tr>
+ <th>タイトル数</th>
+ <td>{{.NumOfTitles}}</td>
+ </tr>
+ <tr>
+ <th>特徴</th>
+ <td>{{raw .Feature}}</td>
+ </tr>
+ <tr>
+ <th>備考</th>
+ <td>{{.Description}}</td>
+ </tr>
+ </table>
+ {{else}}
+ <div class="alert alert-info">data not found</div>
+ {{end}}
+
+ {{with .seriesMap.bronze}}
+ <div class="alert alert-success">bronze series found</div>
+ {{else}}
+ <div class="alert alert-warning">bronze series not found</div>
+ {{end}}
+ </div>
+ </div>
+</div>
+```
+
+**マップはキー名で直接参照できます**
+
+siriesMapは下記のキーを持つマップです。
+
+```go:app.go
+seriesMap := map[string]*Series{
+ "silver": silver,
+ "gold": gold,
+ "wide": wide,
+ "multi": multi,
+ "panorama": panorama,
+ "super": super,
+}
+```
+
+このように直接キー名で参照することができます。
+
+```html:index.html_sample4_map
+{{with .seriesMap.multi}}
+ ...
+{{end}}
+```
+
+indexを使っても同じことができます。
+
+```html:index.html_sample_index
+{{with index .seriesMap "multi"}}
+ ...
+{{end}}
+```
+
+### ネストしたテンプレートを使う
+
+templateアクション(Golang)を使ってテンプレートをネストさせることができます。
+templateアクションの第2引数に指定するデータ構造が、ネストするテンプレート内の`.`が指すデータ構造になります。
+
+```html:Index.html
+{{/* ネストする_gw.htmlテンプレートの.にgamewatchSliceの7番目の要素を設定 */}}
+{{template "App/_gw.html" index .gamewatchSlice 7}}
+```
+
+この例では、ネストするテンプレート内で`.`はgamewatchSliceの7番目の要素を指します。
+
+```html:_gw.htmlテンプレート
+{{/*
+ ネストするテンプレート
+ .は呼び出し元で指定したデータに設定されます。
+*/}}
+<div class="container">
+ <div class="row">
+ <div class="span12">
+ <table class="table table-bordered table-striped">
+ <caption> nested template | sample</caption>
+ <tr>
+ <th class="span2">型番</th>
+ <td class="span10">{{.ModelNumber}}</td>
+ </tr>
+ <tr>
+ <th>タイトル</th>
+ <td>
+ {{/* mapはキーを直接指定することができます */}}
+ <p><span class="label">ja:</span>{{.Title.ja}}</p>
+ <p><span class="label">en:</span>{{.Title.en}}</p>
+ </td>
+ </tr>
+ <tr>
+ <th>発売日</th>
+ {{/* dateで日付をフォーマットすることができます */}}
+ <td>{{date .ReleaseDate}}</td>
+ </tr>
+ <tr>
+ <th>定価</th>
+ <td>
+ {{/* 事前定義の関数呼び出し */}}
+ <p>{{printf "%d円" .Price}}</p>
+ {{/* カスタム定義の関数呼び出し */}}
+ <p>{{CurrencyFormat .Price}}</p>
+ </td>
+ </tr>
+ <tr>
+ <th>シリーズ</th>
+ <td>
+ {{/* | を使ったコマンドの連鎖 */}}
+ <p>{{printf "名称:「%s」 タイトル数:%d 特徴:%s" .Name .NumOfTitles .Feature | raw}}</p>
+ {{/* メソッド呼び出し */}}
+ <p>{{.Series.Description}}</p>
+ </td>
+ </tr>
+ <tr>
+ <th>カラー</th>
+ <td>
+ {{range $color := .VariousColor}}
+ {{if eq $color "ORANGE"}}
+ <span class="label label-warning">{{$color}}</span>
+ {{else if eq $color "WHITE"}}
+ <span class="label">{{$color}}</span>
+ {{else if eq $color "RED"}}
+ <span class="label label-important">{{$color}}</span>
+ {{else if eq $color "BLUE"}}
+ <span class="label label-info">{{$color}}</span>
+ {{else if eq $color "BLACK"}}
+ <span class="label label-inverse">{{$color}}</span>
+ {{else}}
+ <span>{{$color}}</span>
+ {{end}}
+ {{end}}
+ </td>
+ </tr>
+ <tr>
+ <th>ジャンル</th>
+ <td>{{.Genre}}</td>
+ </tr>
+ <tr>
+ <th>キャラクター</th>
+ <td>{{.Character}}</td>
+ </tr>
+ <tr>
+ <th>プレミア</th>
+ <td>
+ {{if .Premium}}
+ {{if eq .Premium "S"}}
+ <span class="badge badge-warning">{{.Premium}}</span>
+ {{else if eq .Premium "A"}}
+ <span class="badge badge-success">{{.Premium}}</span>
+ {{else if eq .Premium "B"}}
+ <span class="badge badge-success">{{.Premium}}</span>
+ {{else if eq .Premium "C"}}
+ <span class="badge badge-info">{{.Premium}}</span>
+ {{else}}
+ <span class="badge">{{.Premium}}</span>
+ {{end}}
+ {{end}}
+ </td>
+ </tr>
+ <tr>
+ <th>ミリオン</th>
+ <td>
+ {{if .Million}}
+ <p class="alert alert-info">100万本以上セールス</p>
+ {{end}}
+ </td>
+ </tr>
+ </table>
+
+ </div>
+ </div>
+ </div>
+</div>
+```
+
+#### メソッドを使う
+
+```html
+{{/* メソッド呼び出し */}}
+<p>{{.Series.Description}}</p>
+```
+
+```go:Description
+func (s Series) Description() template.HTML {
+ return template.HTML(fmt.Sprintf("第%d世代 名称:「%s」 発売タイトル:%d本 特徴:「%s」", s.Generation, s.Name, s.NumOfTitles, s.Feature))
+}
+```
+
+#### date関数
+
+dateはRevelのグローバル関数です。datetimeもあります。
+日付の書式は`app.conf`で変更できます。
+
+```html:_gw.htmlテンプレート
+<th>発売日</th>
+{{/* date関数で日付をフォーマットすることができます */}}
+<td>{{date .ReleaseDate}}</td>
+```
+
+#### printf関数
+
+printfはGolang text/templateのグローバル関数です。print,printlnもあります。
+第1引数にフォーマット、第2引数以降にフォーマットに適用する値を指定します。
+
+```html
+<th>定価</th>
+<td>
+ {{/* 事前定義の関数呼び出し */}}
+ <p>{{printf "%d円" .Price}}</p>
+</td>
+```
+
+#### カスタム関数を使う
+
+独自の関数をtemplateで使用することができます。
+
+**CurrencyFormat**
+
+CurrencyFormat関数はint型のデータをカンマ区切りの文字列に変換し"円"を付加して返します。
+
+```go:app.go
+func CurrencyFormat(price int) string {
+ str := humanize.Comma(int64(price))
+ return str + "円"
+}
+```
+
+**Golangの関数**
+
+Golangの関数をテンプレートから使用したい場合もカスタム関数として登録する必要があります。
+
+
+**カスタム関数の登録**
+
+この関数をテンプレートから使用するには`revel.TemplateFuncs`に名前を付けて登録します。
+2つめの`ToLower`はstringsパッケージのToLowerを登録しています。
+
+```go:app.go
+func init() {
+
+ revel.TemplateFuncs["CurrencyFormat"] = CurrencyFormat
+ revel.TemplateFuncs["ToLower"] = strings.ToLower
+
+ ~省略~
+}
+```
+
+独自関数をテンプレートから使用するには以下のように``{{``と``}}``で囲みます。
+
+```html:CurrencyFormat
+<p>{{CurrencyFormat .Price}}</p>
+```
+
+```html:ToLower
+<p>{{ToLower .}}</p>
+```
+
+#### パイプを使ったコマンドの連鎖
+
+`|`でコマンドを連鎖させることができます。
+
+この例ではprintf関数でフォーマットした文字列をraw関数へ渡しています。
+
+```html:_gw.htmlテンプレート
+<th>シリーズ</th>
+<td>
+ {{/* | を使ったコマンドの連鎖 */}}
+ <p>{{printf "名称:「%s」 発売タイトル:%d本 特徴:「%s」" .Name .NumOfTitles .Feature | raw}}</p>
+</td>
+```
+
+## 補足
+
+### Golang text/templateの基礎
+
+* データ評価(パイプラインや引数)や制御構造(`if`,`with`,`range`)をアクションと呼ぶ。
+* アクションとコメントは`{{`と`}}`で囲む。
+* アクションの外側のテキストはそのまま出力される。
+* データ構造内の現在地を`.`(ドット)と呼ぶ。
+* パイプラインはコマンド(引数、メソッド、関数)を1つ以上つなげたもの。
+* パイプラインは変数を宣言できる。
+
+### RevelのRenderArgについて
+
+RevelがRenderArgにセットするデータには下記のものがあります。
+
+```text:RenderArg
+Revel executes the template using the RenderArgs data
+
+RenderArgs : map[string]interface{}
+```
+
+|field |data type |description |
+|:-------------|:---------------------------|:---------------------------------|
+|errors |map[string]*ValidationError |バリデーションで設定 |
+|flash |map[string]string |FlashParams()メソッドなどで設定 |
+|RunMode |string |アプリケーションの引数-RunModeで設定 |
+|DevMode |bool |app.confで設定 |
+|currentLocale |string |httpヘッダーかcookieの値で設定 |
+
+
+**errors**
+
+errorsは`ValidationFilter`によって、Validation時にセットしたエラーがRenderArgsにセットされます。
+
+> ```go:Validation(抜粋)
+> func ValidationFilter(c *Controller, fc []Filter) {
+> errors, err := restoreValidationErrors(c.Request.Request)
+> c.Validation = &Validation{
+> Errors: errors,
+> keep: false,
+> }
+> hasCookie := (err != http.ErrNoCookie)
+>
+> fc[0](c, fc[1:])
+>
+> // Add Validation errors to RenderArgs.
+> c.RenderArgs["errors"] = c.Validation.ErrorMap()
+>
+> ~省略~
+>
+> }
+> ```
+>
+> [Vaildation] (https://revel.github.io/docs/src/validation.html)
+
+
+**flash**
+
+flashは`revel.FlashFilter`によって、cookie(REVEL_FLASH)に格納された値をRenderArgsへセットします。
+
+> ```go:Flash
+> // FlashFilter is a Revel Filter that retrieves and sets the flash cookie.
+> // Within Revel, it is available as a Flash attribute on Controller instances.
+> // The name of the Flash cookie is set as CookiePrefix + "_FLASH".
+> func FlashFilter(c *Controller, fc []Filter) {
+> c.Flash = restoreFlash(c.Request.Request)
+> c.RenderArgs["flash"] = c.Flash.Data
+>
+> fc[0](c, fc[1:])
+>
+> // Store the flash.
+> var flashValue string
+> for key, value := range c.Flash.Out {
+> flashValue += "\x00" + key + ":" + value + "\x00"
+> }
+> c.SetCookie(&http.Cookie{
+> Name: CookiePrefix + "_FLASH",
+> Value: url.QueryEscape(flashValue),
+> HttpOnly: CookieHttpOnly,
+> Secure: CookieSecure,
+> Path: "/",
+> })
+> }
+> ```
+>
+> [FlashFilter] (https://revel.github.io/docs/src/flash.html)
+
+
+**DevMode / RunMode**
+
+`DevMode`はapp.confの`mode.dev`の値で設定されます。
+`RunMode`はプログラム引数の`-runMode`で指定します。指定がない場合は"dev"が設定されます。通常は"dev"か"prod"の2種類です。
+RunModeはapp.confのセクションと関係があります。RunModeが"prod"の場合app.confの[prod]セクションの設定が有効になります。
+
+```text:app.conf
+[dev]
+# This sets `DevMode` variable to `true` which can be used in your code as
+# `if revel.DevMode {...}`
+# or in your templates with
+# `<no value>`
+mode.dev = true
+```
+
+初期化はInit関数で行われます。
+
+> ```go:Init(抜粋)
+> package revel
+>
+> var (
+> RunMode string // Application-defined (by default, "dev" or "prod")
+> DevMode bool // if true, RunMode is a development mode.
+> )
+>
+> func Init(mode, importPath, srcPath string) {
+>
+> RunMode = mode
+>
+> // Configure properties from app.conf
+> DevMode = Config.BoolDefault("mode.dev", false)
+>
+> }
+> ```
+
+RenderArgsへのセットは`NewController`で行っています。
+
+> ```go:NewController(抜粋)
+> func NewController(req *Request, resp *Response) *Controller {
+> return &Controller{
+> Request: req,
+> Response: resp,
+> Params: new(Params),
+> Args: map[string]interface{}{},
+> RenderArgs: map[string]interface{}{
+> "RunMode": RunMode,
+> "DevMode": DevMode,
+> },
+> }
+> }
+> ```
+
+これによりテンプレート内では以下のような表示内容の切り替えを行うことができます。
+
+```html
+{{if eq .RunMode "dev"}}
+
+~開発用の内容~
+
+{{else}}
+
+~本番用の内容~
+
+{{end}}
+```
+
+[Revel] (https://revel.github.io/docs/godoc/revel.html)
+[Controller] (https://revel.github.io/docs/src/controller.html)
+[app.conf] (https://revel.github.io/manual/appconf.html)
+
+**currentLocale**
+
+currentLocaleは`revel.I18nFilter`によってRenderArgsにセットされます。
+この値はメッセージリソースの参照で利用されます。
+
+> ```go:I18nFilter
+> func I18nFilter(c *Controller, fc []Filter) {
+> if foundCookie, cookieValue := hasLocaleCookie(c.Request); foundCookie {
+> TRACE.Printf("Found locale cookie value: %s", cookieValue)
+> setCurrentLocaleControllerArguments(c, cookieValue)
+> } else if foundHeader, headerValue := hasAcceptLanguageHeader(c.Request); foundHeader {
+> TRACE.Printf("Found Accept-Language header value: %s", headerValue)
+> setCurrentLocaleControllerArguments(c, headerValue)
+> } else {
+> TRACE.Println("Unable to find locale in cookie or header, using empty string")
+> setCurrentLocaleControllerArguments(c, "")
+> }
+> fc[0](c, fc[1:])
+> }
+> ```
+>
+> [I18n] (https://revel.github.io/docs/src/i18n.html)
+
+### Slugについて
+
+SlugはRevelのグローバル関数です。
+どのような機能を提供するのか気になったのですこし調べてみましたが、日本語に対しては使いどころのなさそうな関数でした。
+第1引数に渡す文字列から半角英数字と半角スペース、アンダースコア、ハイフン以外の文字を除去し、大文字は小文字へ、半角スペースはハイフンへ変換します。
+たとえば、`DONKEY KONG JR.`は`donkey-kong-jr`に変換されます。
+
+```html:slug
+<p>
+ {{/* slug関数 */}}
+ {{slug .Title.en}}
+</p>
+```
+
+[Stack Overflow - What is a “slug” in Django?] (http://stackoverflow.com/questions/427102/what-is-a-slug-in-django)
+[Stack Overflow - What is the etymology of 'slug'?] (http://stackoverflow.com/questions/4230846/what-is-the-etymology-of-slug)
+
+### Humane Units
+
+サンプルコードの数値をカンマ区切りで表示する機能で使用しました。
+
+https://github.com/dustin/go-humanize
+
+**インストール**
+
+```bat:install
+go get -u -v github.com/dustin/go-humanize
+```
+
+### サンプルコード
+
+**controllers/app.go**
+
+```go:app.go
+package controllers
+
+import (
+ "fmt"
+ "html/template"
+ "strings"
+ "time"
+
+ "github.com/dustin/go-humanize"
+ "github.com/revel/revel"
+)
+
+type App struct {
+ *revel.Controller
+}
+
+/*
+GameWatch は携帯ゲーム機の情報を持つ
+*/
+type GameWatch struct {
+ No int
+ ModelNumber string
+ Title map[string]string
+ ReleaseDate time.Time
+ Price int
+ *Series
+ Million bool
+ Premium string
+ VariousColor []string
+ Genre string
+ Character string
+}
+
+/*
+Series は携帯ゲーム機のシリーズの情報を持つ
+*/
+type Series struct {
+ Generation int
+ Name string
+ NumOfTitles int
+ Feature string
+ Battery []*ButtonCell
+}
+
+func (s Series) Description() template.HTML {
+ return template.HTML(fmt.Sprintf("第%d世代 名称:「%s」 発売タイトル:%d本 特徴:「%s」", s.Generation, s.Name, s.NumOfTitles, s.Feature))
+}
+
+/*
+ButtonCell は携帯ゲーム機で使用できるボタン電池の情報を持つ
+*/
+type ButtonCell struct {
+ Code string
+ Diameter float32
+ Height float32
+ Voltage float32
+}
+
+func (c App) Index() revel.Result {
+
+ seriesListTitle := "携帯ゲーム機シリーズ一覧"
+ gameWatchListTitle := "携帯ゲーム機一覧"
+
+ /* シリーズのスライス */
+ seriesSlice := []*Series{
+ silver,
+ gold,
+ wide,
+ multi,
+ panorama,
+ newwide,
+ super,
+ }
+
+ /* シリーズのマップ */
+ seriesMap := map[string]*Series{
+ "silver": silver,
+ "gold": gold,
+ "wide": wide,
+ "multi": multi,
+ "panorama": panorama,
+ "super": super,
+ }
+
+ /* ゲーム機のスライス */
+ gamewatchSlice := []*GameWatch{
+ ac01,
+ fl02,
+ mt03,
+ rc04,
+ ip05,
+ mh06,
+ cn07,
+ ln08,
+ pr21,
+ oc22,
+ dk52,
+ dj101,
+ sm91,
+ bu201,
+ }
+
+ labelMap := map[string]interface{}{
+ "title": "Home",
+ "description": "Revel Framework template sample",
+ }
+
+ return c.Render(gamewatchSlice, seriesSlice, seriesMap, gameWatchListTitle, seriesListTitle, labelMap)
+}
+
+func CurrencyFormat(price int) string {
+ str := humanize.Comma(int64(price))
+ return str + "円"
+}
+
+var (
+ lr43, sr43, lr44, sr44 *ButtonCell
+ silver, gold, wide, multi, panorama, newwide, super *Series
+ ac01, fl02, mt03, rc04, ip05, mh06, cn07, ln08, pr21, oc22, dk52, dj101, sm91, bu201 *GameWatch
+)
+
+func init() {
+ fmt.Printf("sample.go init()\n")
+
+ //独自関数の登録
+ revel.TemplateFuncs["CurrencyFormat"] = CurrencyFormat
+ revel.TemplateFuncs["ToLower"] = strings.ToLower
+
+ //電池の準備
+ lr43 = &ButtonCell{
+ Code: "LR43",
+ Diameter: 11.6,
+ Height: 4.2,
+ Voltage: 1.5}
+ sr43 = &ButtonCell{
+ Code: "SR43",
+ Diameter: 11.6,
+ Height: 4.2,
+ Voltage: 1.55}
+ lr44 = &ButtonCell{
+ Code: "LR44",
+ Diameter: 11.6,
+ Height: 5.4,
+ Voltage: 1.5}
+ sr44 = &ButtonCell{
+ Code: "SR44",
+ Diameter: 11.6,
+ Height: 5.4,
+ Voltage: 1.55}
+
+ //シリーズの準備
+ silver = &Series{
+ Generation: 1,
+ Name: "シルバー",
+ NumOfTitles: 5,
+ Feature: `本体前面のプレートが<b>シルバー</b><br/>最初期のシリーズ`,
+ Battery: []*ButtonCell{lr43, sr43}}
+ gold = &Series{
+ Generation: 2,
+ Name: "ゴールド",
+ NumOfTitles: 3,
+ Feature: `本体前面のプレートが<b>ゴールド</b><br/>アラーム機能、背面にスタンド付き`,
+ Battery: []*ButtonCell{lr43, sr43}}
+ wide = &Series{
+ Generation: 3,
+ Name: "ワイドスクリーン",
+ NumOfTitles: 9,
+ Feature: `画面サイズが従来シリーズの"1.7倍"<br/>GAME A/GAME B/TIMEボタンの配置が本体右上に変更された`,
+ Battery: []*ButtonCell{lr43, sr43}}
+ multi = &Series{
+ Generation: 4,
+ Name: "マルチスクリーン",
+ NumOfTitles: 8,
+ Feature: `本体が折りたためる2画面構成<br/>上下に開くタイプと左右に開くタイプがある`,
+ Battery: []*ButtonCell{lr44, sr44}}
+ panorama = &Series{
+ Generation: 5,
+ Name: "パノラマスクリーン",
+ NumOfTitles: 4,
+ Feature: `カラースクリーンテーブルトップを携帯サイズにした改良型`,
+ Battery: []*ButtonCell{lr44, sr44}}
+ newwide = &Series{
+ Generation: 6,
+ Name: "ニューワイド",
+ NumOfTitles: 2,
+ Feature: `ワイドスクリーンの廉価版<br/>マルチスクリーンと発売時期が重なる`,
+ Battery: []*ButtonCell{lr44, sr44}}
+ super = &Series{
+ Generation: 7,
+ Name: "スーパーカラー",
+ NumOfTitles: 2,
+ Feature: `縦長サイズでセロファンを貼った疑似カラー表示`,
+ Battery: []*ButtonCell{lr44, sr44}}
+
+ lc, _ := time.LoadLocation("Asia/Tokyo")
+
+ //携帯ゲーム機の準備
+ ac01 = &GameWatch{
+ No: 1,
+ ModelNumber: "AC-01",
+ Title: map[string]string{"ja": "ボール", "en": "BALL"},
+ ReleaseDate: time.Date(1980, 4, 28, 0, 0, 0, 0, lc),
+ Price: 5800,
+ Series: silver,
+ Million: false,
+ Premium: "A",
+ VariousColor: []string{"RED"},
+ Genre: "お手玉",
+ Character: "ロボット",
+ }
+
+ fl02 = &GameWatch{
+ No: 2,
+ ModelNumber: "FL-02",
+ Title: map[string]string{"ja": "フラッグマン", "en": "FLAGMAN"},
+ ReleaseDate: time.Date(1980, 6, 05, 0, 0, 0, 0, lc),
+ Price: 5800,
+ Series: silver,
+ Million: false,
+ Premium: "S",
+ VariousColor: []string{"ORANGE"},
+ Genre: "数字当て",
+ Character: "フラッグマン",
+ }
+
+ mt03 = &GameWatch{
+ No: 3,
+ ModelNumber: "MT-03",
+ Title: map[string]string{"ja": "バーミン", "en": "VERMIN"},
+ ReleaseDate: time.Date(1980, 7, 10, 0, 0, 0, 0, lc),
+ Price: 5800,
+ Series: silver,
+ Million: true,
+ Premium: "A",
+ VariousColor: []string{"WHITE", "BLUE", "BLACK"},
+ Genre: "モグラ叩き",
+ Character: "モグラを叩く人",
+ }
+
+ rc04 = &GameWatch{
+ No: 4,
+ ModelNumber: "RC-04",
+ Title: map[string]string{"ja": "ファイア", "en": "FIRE"},
+ ReleaseDate: time.Date(1980, 7, 31, 0, 0, 0, 0, lc),
+ Price: 5800,
+ Series: silver,
+ Million: true,
+ Premium: "A",
+ VariousColor: []string{"BLUE"},
+ Genre: "人命救助",
+ Character: "救急隊員",
+ }
+
+ ip05 = &GameWatch{
+ No: 5,
+ ModelNumber: "IP-05",
+ Title: map[string]string{"ja": "ジャッジ", "en": "JUDGE"},
+ ReleaseDate: time.Date(1980, 10, 4, 0, 0, 0, 0, lc),
+ Price: 5800,
+ Series: silver,
+ Million: true,
+ Premium: "A",
+ VariousColor: []string{"GREEN", "PURPLE"},
+ Genre: "なぐりっこ",
+ Character: "対戦する兄弟",
+ }
+
+ mh06 = &GameWatch{
+ No: 6,
+ ModelNumber: "MH-06",
+ Title: map[string]string{"ja": "マンホール", "en": "MANHOLE"},
+ ReleaseDate: time.Date(1981, 1, 27, 0, 0, 0, 0, lc),
+ Price: 5800,
+ Series: gold,
+ Million: true,
+ Premium: "B",
+ VariousColor: []string{"BROWN"},
+ Genre: "人命救助",
+ Character: "作業員",
+ }
+
+ cn07 = &GameWatch{
+ No: 7,
+ ModelNumber: "CN-07",
+ Title: map[string]string{"ja": "ヘルメット", "en": "HELMET"},
+ ReleaseDate: time.Date(1981, 2, 21, 0, 0, 0, 0, lc),
+ Price: 5800,
+ Series: gold,
+ Million: true,
+ Premium: "B",
+ VariousColor: []string{"RED"},
+ Genre: "荷物運び",
+ Character: "作業員",
+ }
+
+ ln08 = &GameWatch{
+ No: 8,
+ ModelNumber: "LN-08",
+ Title: map[string]string{"ja": "ライオン", "en": "LION"},
+ ReleaseDate: time.Date(1981, 4, 27, 0, 0, 0, 0, lc),
+ Price: 5800,
+ Series: gold,
+ Million: false,
+ Premium: "B",
+ VariousColor: []string{"RED"},
+ Genre: "動物監視",
+ Character: "飼育員",
+ }
+
+ pr21 = &GameWatch{
+ No: 9,
+ ModelNumber: "PR-21",
+ Title: map[string]string{"ja": "パラシュート", "en": "PARACHUTE"},
+ ReleaseDate: time.Date(1981, 6, 19, 0, 0, 0, 0, lc),
+ Price: 5800,
+ Series: wide,
+ Million: true,
+ Premium: "C",
+ VariousColor: []string{"BROWN"},
+ Genre: "人命救助",
+ Character: "ボートを漕ぐ人",
+ }
+
+ oc22 = &GameWatch{
+ No: 10,
+ ModelNumber: "OC-22",
+ Title: map[string]string{"ja": "オクトパス", "en": "OCTOPUS"},
+ ReleaseDate: time.Date(1981, 7, 16, 0, 0, 0, 0, lc),
+ Price: 6000,
+ Series: wide,
+ Million: true,
+ Premium: "C",
+ VariousColor: []string{"RED"},
+ Genre: "トレジャーハンター",
+ Character: "潜水夫",
+ }
+
+ dk52 = &GameWatch{
+ No: 19,
+ ModelNumber: "DK-52",
+ Title: map[string]string{"ja": "ドンキーコング", "en": "DONKEY KONG"},
+ ReleaseDate: time.Date(1982, 6, 3, 0, 0, 0, 0, lc),
+ Price: 6000,
+ Series: multi,
+ Million: true,
+ Premium: "C",
+ VariousColor: []string{"ORANGE"},
+ Genre: "タル避け",
+ Character: "救助マン",
+ }
+
+ dj101 = &GameWatch{
+ No: 20,
+ ModelNumber: "DJ-101",
+ Title: map[string]string{"ja": "ドンキーコングJR.", "en": "DONKEY KONG JR."},
+ ReleaseDate: time.Date(1982, 10, 26, 0, 0, 0, 0, lc),
+ Price: 4800,
+ Series: newwide,
+ Million: true,
+ Premium: "B",
+ VariousColor: []string{"WHITE+GREEN"},
+ Genre: "コング救出",
+ Character: "ジュニア",
+ }
+
+ sm91 = &GameWatch{
+ No: 26,
+ ModelNumber: "SM-91",
+ Title: map[string]string{"ja": "スヌーピー", "en": "SNOOPY"},
+ ReleaseDate: time.Date(1983, 8, 30, 0, 0, 0, 0, lc),
+ Price: 6000,
+ Series: panorama,
+ Million: false,
+ Premium: "A",
+ VariousColor: []string{"YELLOW"},
+ Genre: "音符叩き",
+ Character: "スヌーピー",
+ }
+
+ bu201 = &GameWatch{
+ No: 31,
+ ModelNumber: "BU-201",
+ Title: map[string]string{"ja": "スピットボール スパーキー", "en": "SPITBALL SPARKY"},
+ ReleaseDate: time.Date(1984, 2, 7, 0, 0, 0, 0, lc),
+ Price: 6000,
+ Series: super,
+ Million: false,
+ Premium: "C",
+ VariousColor: []string{"SILVER"},
+ Genre: "ブロック崩し",
+ Character: "スパーキー",
+ }
+
+}
+```
+
+**views/App/Index.html**
+
+```html:Index.html
+{{set . "title" "Home"}}
+{{set . "description" "Revel Framework template sample"}}
+{{template "header.html" .}}
+
+{{set .labelMap "keyword" "set function example"}}
+
+{{append . "moreStyles" "css/index.custom.css"}}
+
+
+<header>
+ <div class="container">
+ <div class="row">
+ <h1>Revel templates sample code!</h1>
+ <div class="span12">
+ {{with .labelMap}}
+ <p>{{.title}}</p>
+ <p>{{.description}}</p>
+ {{end}}
+ </div>
+ </div>
+ </div>
+</header>
+
+<div class="container">
+ <div class="row">
+ <div class="span12">
+ {{template "flash.html" .}}
+ </div>
+ </div>
+</div>
+
+<!-- シリーズ 一覧-->
+{{/* sample 1 range */}}
+<div class="container">
+ <div class="row">
+ <div class="span12">
+ <table class="table table-bordered table-striped">
+ <caption>{{.seriesListTitle}} | sample 1</caption>
+ <thead>
+ <tr>
+ <th>idx</th>
+ <th>世代</th>
+ <th>名称</th>
+ <th>タイトル数</th>
+ <th>電池</th>
+ <th>特徴</th>
+ </tr>
+ </thead>
+ <tbody>
+ {{/* rangeブロック内ではseriesの各要素をドット(.)で参照する */}}
+ {{range .seriesSlice}}
+ <tr>
+ <td></td>
+ <td>{{.Generation}}</td>
+ <td>{{.Name}}</td>
+ <td>{{.NumOfTitles}}</td>
+ <td>
+ {{if .Battery}}
+ <ul>
+ {{/* ネストしたrange */}}
+ {{range .Battery}}
+ <li>{{pad .Code 10}}({{.Voltage}}v)</li>
+ {{end}}
+ </ul>
+ {{end}}
+ </td>
+ <td>
+ {{/* raw関数でエスケープ処理を回避 */}}
+ {{raw .Feature}}
+ </td>
+ </tr>
+ {{end}}
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+<!--シリーズ一覧-->
+
+<!-- シリーズ 一覧-->
+{{/* sample 2 range */}}
+<div class="container">
+ <div class="row">
+ <div class="span12">
+ <table class="table table-bordered table-striped">
+ <caption>{{.seriesListTitle}} | sample 2</caption>
+ <thead>
+ <tr>
+ <th>idx</th>
+ <th>世代</th>
+ <th>名称</th>
+ <th>タイトル数</th>
+ <th>電池</th>
+ <th>特徴</th>
+ </tr>
+ </thead>
+ <tbody>
+ {{/* ドット(.)の代わりに変数名($value)で参照する */}}
+ {{range $index, $value := .seriesSlice}}
+ {{/* $indexとeventを使って隔行ごとにclassを変える */}}
+ <tr class="{{if even $index}}info{{else}}{{end}}">
+ <td>{{$index}}</td>
+ <td>{{$value.Generation}}</td>
+ <td>{{$value.Name}}</td>
+ <td>{{$value.NumOfTitles}}</td>
+ <td>
+ {{if .Battery}}
+ {{/* ネストしたrange */}}
+ <ul>
+ {{range $cell := .Battery}}
+ {{/*<li>{{$cell.Code}} {{pad "" 2}} ({{$cell.Voltage}}v)</li>*/}}
+ <li>{{pad $cell.Code 10}}({{$cell.Voltage}}v)</li>
+ {{end}}
+ </ul>
+ {{end}}
+ </td>
+ <td>
+ {{/* raw関数でエスケープ処理を回避 */}}
+ {{raw $value.Feature}}
+ </td>
+ </tr>
+ {{end}}
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+<!--シリーズ一覧-->
+
+<!--シリーズ -->
+{{/* sample 3 index */}}
+<div class="container">
+ <div class="row">
+ <div class="span12">
+ {{/* indexで添え字を指定して特定要素を参照できる */}}
+ {{$idx := 1}}
+ {{$sa := index .seriesSlice $idx}}
+ <table class="table table-bordered table-striped">
+ <caption>ゴールド | sample 3</caption>
+ <tr>
+ <th>世代</th>
+ <td>{{$sa.Generation}}</td>
+ </tr>
+ <tr>
+ <th>名称</th>
+ <td>{{$sa.Name}}</td>
+ </tr>
+ <tr>
+ <th>タイトル数</th>
+ <td>{{$sa.NumOfTitles}}</td>
+ </tr>
+ <tr>
+ <th>特徴</th>
+ <td>{{raw $sa.Feature}}</td>
+ </tr>
+ <tr>
+ <th>備考</th>
+ <td>{{$sa.Description}}</td>
+ </tr>
+ </table>
+ </div>
+ </div>
+</div>
+
+{{/* sample 4 with */}}
+<div class="container">
+ <div class="row">
+ <div class="span12">
+ {{/*
+ mapのキーで参照できる
+ withはifと似ているがドットに影響を与える
+ */}}
+ {{with index .seriesMap "multi"}}
+ <table class="table table-bordered table-striped">
+ <caption>マルチスクリーン | sample 4</caption>
+ <tr>
+ <th>世代</th>
+ <td>{{.Generation}}</td>
+ </tr>
+ <tr>
+ <th>名称</th>
+ <td>{{.Name}}</td>
+ </tr>
+ <tr>
+ <th>タイトル数</th>
+ <td>{{.NumOfTitles}}</td>
+ </tr>
+ <tr>
+ <th>特徴</th>
+ <td>{{raw .Feature}}</td>
+ </tr>
+ <tr>
+ <th>備考</th>
+ <td>{{.Description}}</td>
+ </tr>
+ </table>
+ {{else}}
+ <div class="alert alert-info">data not found</div>
+ {{end}}
+
+ {{with .seriesMap.bronze}}
+ <div class="alert alert-success">bronze series found</div>
+ {{else}}
+ <div class="alert alert-warning">bronze series not found</div>
+ {{end}}
+ </div>
+ </div>
+</div>
+
+<!--ゲーム機一覧-->
+{{/* sample 5 */}}
+<div class="container">
+ <div class="row">
+ <div class="span12">
+ <table class="table table-bordered table-striped">
+ <caption>{{.gameWatchListTitle}} | sample 5</caption>
+ <thead>
+ <tr>
+ <th>No</th>
+ <th>型番</th>
+ <th>タイトル</th>
+ <th>発売日</th>
+ <th>定価</th>
+ <th>シリーズ</th>
+ <th>バリエーション数</th>
+ <th>カラー</th>
+ </tr>
+ </thead>
+ <tbody>
+ {{range .gamewatchSlice}}
+ <tr>
+ <td>{{.No}}</td>
+ <td>{{.ModelNumber}}</td>
+ <td>
+ <p>{{.Title.ja}}</p>
+ <p>
+ {{/* slug関数 */}}
+ {{slug .Title.en}}
+ </p>
+ </td>
+ <td>
+ {{/* date関数で日付をフォーマットすることができます */}}
+ {{date .ReleaseDate}}
+ </td>
+ <td>
+ {{/* 比較演算子 */}}
+ {{if ge .Price 6000}}
+ <span style="color:red">*</span>{{.Price}}
+ {{else if lt .Price 5000}}
+ {{.Price}}
+ {{else}}
+ <span style="color:blue">*</span>{{.Price}}
+ {{end}}
+ </td>
+ <td>
+ {{.Name}}
+ </td>
+ <td>
+ {{/* len関数で要素数をカウントする */}}
+ {{len .VariousColor}}
+ </td>
+ <td>
+ {{range .VariousColor}}
+ {{/* カスタム関数の呼び出し */}}
+ <p>{{ToLower .}}</p>
+ {{end}}
+ </td>
+ </tr>
+ {{end}}
+ </tbody>
+ </table>
+ </div>
+ </div>
+
+</div>
+<!--ゲーム機一覧-->
+
+<div class="container">
+ <div class="row">
+ <div class="span12">
+
+ {{$gw := index .gamewatchSlice 5}}
+
+ <table class="table table-bordered table-striped">
+ <caption> MH-06 | sample </caption>
+ <tr>
+ <th class="span2">型番</th>
+ <td class="span10">{{$gw.ModelNumber}}</td>
+ </tr>
+ <tr>
+ <th>タイトル</th>
+ <td>
+ {{/* mapはキーを直接指定することができます */}}
+ <p><span class="label">ja:</span>{{$gw.Title.ja}}</p>
+ <p><span class="label">en:</span>{{$gw.Title.en}}</p>
+ </td>
+ </tr>
+ <tr>
+ <th>発売日</th>
+ <td>
+ {{/* dateで日付をフォーマットすることができます */}}
+ {{date $gw.ReleaseDate}}
+ </td>
+ </tr>
+ <tr>
+ <th>定価</th>
+ <td>
+ {{/* printf関数の呼び出し */}}
+ <p>{{printf "%d円" $gw.Price}}</p>
+ {{/* カスタム定義の関数呼び出し */}}
+ <p>{{CurrencyFormat $gw.Price}}</p>
+ </td>
+ </tr>
+ <tr>
+ <th>シリーズ</th>
+ <td>
+ {{/* | を使ったコマンドの連鎖 */}}
+ <p>{{printf "名称:「%s」 タイトル数:%d 特徴:%s" $gw.Name $gw.NumOfTitles $gw.Feature | raw}}</p>
+ {{/* メソッド呼び出し */}}
+ <p>{{$gw.Series.Description}}</p>
+ </td>
+ </tr>
+ <tr>
+ <th>カラー</th>
+ <td>
+ {{range $color := $gw.VariousColor}}
+ {{if eq $color "ORANGE"}}
+ <span class="label label-warning">{{$color}}</span>
+ {{else if eq $color "WHITE"}}
+ <span class="label">{{$color}}</span>
+ {{else if eq $color "RED"}}
+ <span class="label label-important">{{$color}}</span>
+ {{else if eq $color "BLUE"}}
+ <span class="label label-info">{{$color}}</span>
+ {{else if eq $color "BLACK"}}
+ <span class="label label-inverse">{{$color}}</span>
+ {{else}}
+ <span>{{$color}}</span>
+ {{end}}
+ {{end}}
+ </td>
+ </tr>
+ <tr>
+ <th>ジャンル</th>
+ <td>{{$gw.Genre}}</td>
+ </tr>
+ <tr>
+ <th>キャラクター</th>
+ <td>{{$gw.Character}}</td>
+ </tr>
+ <tr>
+ <th>プレミア</th>
+ <td>
+ {{if $gw.Premium}}
+ {{if eq $gw.Premium "S"}}
+ <span class="badge badge-warning">{{$gw.Premium}}</span>
+ {{else if eq $gw.Premium "A"}}
+ <span class="badge badge-success">{{$gw.Premium}}</span>
+ {{else if eq $gw.Premium "B"}}
+ <span class="badge badge-success">{{$gw.Premium}}</span>
+ {{else if eq $gw.Premium "C"}}
+ <span class="badge badge-info">{{$gw.Premium}}</span>
+ {{else}}
+ <span class="badge">{{$gw.Premium}}</span>
+ {{end}}
+ {{end}}
+ </td>
+ </tr>
+ <tr>
+ <th>ミリオン</th>
+ <td>
+ {{if $gw.Million}}
+ <p class="alert alert-info">100万本以上セールス</p>
+ {{end}}
+ </td>
+ </tr>
+ </table>
+
+ </div>
+ </div>
+ </div>
+</div>
+
+{{/* ネストする_gw.htmlテンプレートの.にgamewatchSliceの7番目の要素を設定 */}}
+{{template "App/_gw.html" index .gamewatchSlice 7}}
+
+{{template "footer.html" .}}
+```
+
+**views/App/_gw.html**
+
+```html:_gw.html
+{{/*
+ ネストするテンプレート
+ .は呼び出し元で指定したデータに設定されます。
+*/}}
+<div class="container">
+ <div class="row">
+ <div class="span12">
+ <table class="table table-bordered table-striped">
+ <caption> nested template | sample</caption>
+ <tr>
+ <th class="span2">型番</th>
+ <td class="span10">{{.ModelNumber}}</td>
+ </tr>
+ <tr>
+ <th>タイトル</th>
+ <td>
+ {{/* mapはキーを直接指定することができます */}}
+ <p><span class="label">ja:</span>{{.Title.ja}}</p>
+ <p><span class="label">en:</span>{{.Title.en}}</p>
+ </td>
+ </tr>
+ <tr>
+ <th>発売日</th>
+ {{/* date関数で日付をフォーマットすることができます */}}
+ <td>{{date .ReleaseDate}}</td>
+ </tr>
+ <tr>
+ <th>定価</th>
+ <td>
+ {{/* 事前定義の関数呼び出し */}}
+ <p>{{printf "%d円" .Price}}</p>
+ {{/* カスタム定義の関数呼び出し */}}
+ <p>{{CurrencyFormat .Price}}</p>
+ </td>
+ </tr>
+ <tr>
+ <th>シリーズ</th>
+ <td>
+ {{/* | を使ったコマンドの連鎖 */}}
+ <p>{{printf "名称:「%s」 発売タイトル:%d本 特徴:「%s」" .Name .NumOfTitles .Feature | raw}}</p>
+ {{/* メソッド呼び出し */}}
+ <p>{{.Series.Description}}</p>
+ </td>
+ </tr>
+ <tr>
+ <th>カラー</th>
+ <td>
+ {{range $color := .VariousColor}}
+ {{if eq $color "ORANGE"}}
+ <span class="label label-warning">{{$color}}</span>
+ {{else if eq $color "WHITE"}}
+ <span class="label">{{$color}}</span>
+ {{else if eq $color "RED"}}
+ <span class="label label-important">{{$color}}</span>
+ {{else if eq $color "BLUE"}}
+ <span class="label label-info">{{$color}}</span>
+ {{else if eq $color "BLACK"}}
+ <span class="label label-inverse">{{$color}}</span>
+ {{else}}
+ <span>{{$color}}</span>
+ {{end}}
+ {{end}}
+ </td>
+ </tr>
+ <tr>
+ <th>ジャンル</th>
+ <td>{{.Genre}}</td>
+ </tr>
+ <tr>
+ <th>キャラクター</th>
+ <td>{{.Character}}</td>
+ </tr>
+ <tr>
+ <th>プレミア</th>
+ <td>
+ {{if .Premium}}
+ {{if eq .Premium "S"}}
+ <span class="badge badge-warning">{{.Premium}}</span>
+ {{else if eq .Premium "A"}}
+ <span class="badge badge-success">{{.Premium}}</span>
+ {{else if eq .Premium "B"}}
+ <span class="badge badge-success">{{.Premium}}</span>
+ {{else if eq .Premium "C"}}
+ <span class="badge badge-info">{{.Premium}}</span>
+ {{else}}
+ <span class="badge">{{.Premium}}</span>
+ {{end}}
+ {{end}}
+ </td>
+ </tr>
+ <tr>
+ <th>ミリオン</th>
+ <td>
+ {{if .Million}}
+ <p class="alert alert-info">100万本以上セールス</p>
+ {{end}}
+ </td>
+ </tr>
+ </table>
+
+ </div>
+ </div>
+ </div>
+</div>
+```