post Image
Revel templatesを使ったサンプルアプリケーション

概要

Revel frameworkのtemplatesとGolangのhtml/templateパッケージの機能を使ったwebアプリケーションのサンプルコードです。
サンプルコードを題材にテンプレートの機能の使い方を説明していきます。そのため複雑な処理は行わずにコントローラー側でデータを準備しそれをテンプレート側で出力する簡単なアプリケーションになります。

環境

  • Windows7 (64bit)
  • The Go Programming Language 1.4.2
  • Revel 0.12.0

参考サイト

Golang

Revel

etc

アプリケーションの雛形作成

アプリケーション名をtsp (template sample)としました。
newコマンドで雛形を生成しapp.goとIndex.html、_gw.htmlにサンプルコードを追加していきます。

new
> revel new tsp
tsp (アプリケーションROOT)
  |
  +--- /app
  |      |
  |      +--- /controllers
  |      |        |
  |      |        +--- app.go (サンプルコード)
  |
  +--- /views
         |
         +--- /App
                |
                +--- _gw.html (追加したテンプレート)
                +--- Index.html (サンプルコード)

完成図

このような感じのページが出力されます。

image01.png

image02.png

image03.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の一部を抜粋したコードです。(全コードは記事のおわりに掲載しています)

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, pp23, fp24, mc25, fr27,
    dk52, gh54, jr55,
    dj101, sm91, pg92, bu201, ud202, bj60,
  }

  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, pp23, fp24, mc25, fr27,
  dk52, gh54, jr55, dj101, sm91, pg92, bu201, ud202, bj60 *GameWatch
)

func init() {

  //独自関数の登録
  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へ追加されテンプレートから参照できるようになります。

Controller(抜粋)
type Controller struct {

  RenderArgs map[string]interface{} // Args passed to the template.

}
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”という要素を追加します。

index.html
{{set . "title" "Home"}}
{{set . "description" "Revel Framework template sample"}}
{{template "header.html" .}}
header.html
<title>{{.title}}</title>

RenderArgsに無闇に要素を追加したくないといったニーズがあると思いますが、そのときは別途map[string]interface{}型の要素を定義することで対応できます。
このサンプルはlabelMapにテンプレート内から新しい要素を追加します。

app.go_labelMap(抜粋)
labelMap := map[string]interface{}{
  "title":       "Home",
  "description": "Revel Framework template sample",
}

c.Render(gamewatchSlice, seriesSlice, seriesMap, gameWatchListTitle, seriesListTitle, labelMap)
index.html
{{set .labelMap "keyword" "set function example"}}

append関数

appendはRevelのグローバル関数です。
テンプレート内からデータ構造に新しいデータを追加することができます。
第1引数に指定するデータ構造に、第2引数で指定する名前の配列へ第3引数で指定する値を追加します。

これは雛形のheader.htmlにあるページ固有のcssファイルを読み込むサンプルです。
ページごとに異なるcssでレイアウトを変えたい場合などの用途で使用できます。

index.html
{{append . "moreStyles" "css/index.custom.css"}}
header.html
{{range .moreStyles}}
  <link rel="stylesheet" type="text/css" href="/public/{{.}}">
{{end}}

debug.html

雛形に含まれるdebug.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

rangeを使ったループ処理

rangeアクション(Golang)で配列、マップ、スライスなどをループ処理することができます。

このサンプルはseriesSliceをループ処理してテーブルとして出力します。
rangeアクションのブロック内で.が指すデータ構造はseriesSliceの個々の要素になります。
ネストする内側のrangeのブロックと外側のrangeのブロックでは.が指すデータ構造は切り替わります。

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とおなじになります。

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 $value.Battery}}
      {{/* ネストしたrange */}}
      <ul>
      {{range $cell := $value.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;を付加します。

pad
<li>{{pad .Code 10}}({{.Voltage}}v)</li>

raw関数

rawはRevelのグローバル関数です。
第1引数に指定した値をhtmlエスケープせずに出力します。

raw
<td>
  {{/* raw関数でエスケープ処理を回避 */}}
  {{raw .Feature}}
</td>

even関数

evenはRevelのグローバル関数です。
第1引数に指定した数値が偶数の場合はtrue、奇数の場合はfalseを返します。

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変数を使用していますが、リテラルも使用できます。

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関数などと組み合わせることができます。

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を指します。

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は下記のキーを持つマップです。

app.go
seriesMap := map[string]*Series{
  "silver":   silver,
  "gold":     gold,
  "wide":     wide,
  "multi":    multi,
  "panorama": panorama,
  "super":    super,
}

このように直接キー名で参照することができます。

index.html_sample4_map
{{with .seriesMap.multi}}
 ...
{{end}}

indexを使っても同じことができます。

index.html_sample_index
{{with index .seriesMap "multi"}}
 ...
{{end}}

ネストしたテンプレートを使う

templateアクション(Golang)を使ってテンプレートをネストさせることができます。
templateアクションの第2引数に指定するデータ構造が、ネストするテンプレート内の.が指すデータ構造になります。

Index.html
{{/* ネストする_gw.htmlテンプレートの.にgamewatchSliceの7番目の要素を設定 */}}
{{template "App/_gw.html" index .gamewatchSlice 7}}

この例では、ネストするテンプレート内で.はgamewatchSliceの7番目の要素を指します。

_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>

メソッドを使う

{{/* メソッド呼び出し */}}
<p>{{.Series.Description}}</p>
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で変更できます。

_gw.htmlテンプレート
<th>発売日</th>
{{/* date関数で日付をフォーマットすることができます */}}
<td>{{date .ReleaseDate}}</td>

printf関数

printfはGolang text/templateのグローバル関数です。print,printlnもあります。
第1引数にフォーマット、第2引数以降にフォーマットに適用する値を指定します。

<th>定価</th>
<td>
  {{/* 事前定義の関数呼び出し */}}
  <p>{{printf "%d円" .Price}}</p>
</td>

カスタム関数を使う

独自の関数をtemplateで使用することができます。

CurrencyFormat

CurrencyFormat関数はint型のデータをカンマ区切りの文字列に変換し”円”を付加して返します。

app.go
func CurrencyFormat(price int) string {
  str := humanize.Comma(int64(price))
  return str + "円"
}

Golangの関数

Golangの関数をテンプレートから使用したい場合もカスタム関数として登録する必要があります。

カスタム関数の登録

この関数をテンプレートから使用するにはrevel.TemplateFuncsに名前を付けて登録します。
2つめのToLowerはstringsパッケージのToLowerを登録しています。

app.go
func init() {

  revel.TemplateFuncs["CurrencyFormat"] = CurrencyFormat
  revel.TemplateFuncs["ToLower"] = strings.ToLower

  省略
}

独自関数をテンプレートから使用するには以下のように{{}}で囲みます。

CurrencyFormat
<p>{{CurrencyFormat .Price}}</p>
ToLower
<p>{{ToLower .}}</p>

パイプを使ったコマンドの連鎖

|でコマンドを連鎖させることができます。

この例ではprintf関数でフォーマットした文字列をraw関数へ渡しています。

_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にセットするデータには下記のものがあります。

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にセットされます。

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

flash

flashはrevel.FlashFilterによって、cookie(REVEL_FLASH)に格納された値をRenderArgsへセットします。

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

DevMode / RunMode

DevModeはapp.confのmode.devの値で設定されます。
RunModeはプログラム引数の-runModeで指定します。指定がない場合は”dev”が設定されます。通常は”dev”か”prod”の2種類です。
RunModeはapp.confのセクションと関係があります。RunModeが”prod”の場合app.confの[prod]セクションの設定が有効になります。

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関数で行われます。

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で行っています。

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,
        },
    }
}

これによりテンプレート内では以下のような表示内容の切り替えを行うことができます。

{{if eq .RunMode "dev"}}

~開発用の内容~

{{else}}

~本番用の内容~

{{end}}

Revel
Controller
app.conf

currentLocale

currentLocaleはrevel.I18nFilterによってRenderArgsにセットされます。
この値はメッセージリソースの参照で利用されます。

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

Slugについて

SlugはRevelのグローバル関数です。
どのような機能を提供するのか気になったのですこし調べてみましたが、日本語に対しては使いどころのなさそうな関数でした。
第1引数に渡す文字列から半角英数字と半角スペース、アンダースコア、ハイフン以外の文字を除去し、大文字は小文字へ、半角スペースはハイフンへ変換します。
たとえば、DONKEY KONG JR.donkey-kong-jrに変換されます。

slug
<p>
  {{/* slug関数 */}}
  {{slug .Title.en}}
</p>

Stack Overflow – What is a “slug” in Django?
Stack Overflow – What is the etymology of ‘slug’?

Humane Units

サンプルコードの数値をカンマ区切りで表示する機能で使用しました。

https://github.com/dustin/go-humanize

インストール

install
go get -u -v github.com/dustin/go-humanize

サンプルコード

controllers/app.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, pp23, fp24, mc25, fr27,
    dk52, gh54, jr55,
    dj101, sm91, pg92, bu201, ud202, bj60,
  }

  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, pp23, fp24, mc25, fr27,
  dk52, gh54, jr55, dj101, sm91, pg92, bu201, ud202, bj60 *GameWatch
)

func init() {

  //独自関数の登録
  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:    "潜水夫",
}

pp23 = &GameWatch{
  No:           11,
  ModelNumber:  "PP-23",
  Title:        map[string]string{"ja": "ポパイ", "en": "POPEYE"},
  ReleaseDate:  time.Date(1981, 8, 5, 0, 0, 0, 0, lc),
  Price:        6000,
  Series:       wide,
  Million:      true,
  Premium:      "C",
  VariousColor: []string{"GREEN"},
  Genre:        "オリーブとデート",
  Character:    "ポパイ",
}

fp24 = &GameWatch{
  No:           12,
  ModelNumber:  "FP-24",
  Title:        map[string]string{"ja": "シェフ", "en": "CHEF"},
  ReleaseDate:  time.Date(1981, 9, 8, 0, 0, 0, 0, lc),
  Price:        6000,
  Series:       wide,
  Million:      true,
  Premium:      "B",
  VariousColor: []string{"RED"},
  Genre:        "料理",
  Character:    "シェフ",
}

mc25 = &GameWatch{
  No:           13,
  ModelNumber:  "MC-25",
  Title:        map[string]string{"ja": "ミッキーマウス", "en": "MICKEY MOUSE"},
  ReleaseDate:  time.Date(1981, 10, 9, 0, 0, 0, 0, lc),
  Price:        6000,
  Series:       wide,
  Million:      true,
  Premium:      "C",
  VariousColor: []string{"RED"},
  Genre:        "卵回収",
  Character:    "ミッキーマウス",
}

fr27 = &GameWatch{
  No:           14,
  ModelNumber:  "FR-27",
  Title:        map[string]string{"ja": "ファイア", "en": "FIRE"},
  ReleaseDate:  time.Date(1981, 12, 4, 0, 0, 0, 0, lc),
  Price:        6000,
  Series:       wide,
  Million:      true,
  Premium:      "B",
  VariousColor: []string{"BLUE", "GREEN"},
  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:    "ジュニア",
}

gh54 = &GameWatch{
  No:           22,
  ModelNumber:  "GH-54",
  Title:        map[string]string{"ja": "グリーンハウス", "en": "GREEN HOUSE"},
  ReleaseDate:  time.Date(1982, 12, 6, 0, 0, 0, 0, lc),
  Price:        6000,
  Series:       multi,
  Million:      true,
  Premium:      "B",
  VariousColor: []string{"GREEN"},
  Genre:        "虫退治",
  Character:    "スプレーを持った男",
}

jr55 = &GameWatch{
  No:           23,
  ModelNumber:  "JR-55",
  Title:        map[string]string{"ja": "ドンキーコングII", "en": "DONKEY KONG II"},
  ReleaseDate:  time.Date(1983, 3, 7, 0, 0, 0, 0, lc),
  Price:        6000,
  Series:       multi,
  Million:      true,
  Premium:      "B",
  VariousColor: []string{"BROWN"},
  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:    "スヌーピー",
}

pg92 = &GameWatch{
  No:           27,
  ModelNumber:  "PG-92",
  Title:        map[string]string{"ja": "ポパイ", "en": "POPEYE"},
  ReleaseDate:  time.Date(1983, 8, 30, 0, 0, 0, 0, lc),
  Price:        6000,
  Series:       panorama,
  Million:      false,
  Premium:      "A",
  VariousColor: []string{"GREEN"},
  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:    "スパーキー",
}

ud202 = &GameWatch{
  No:           32,
  ModelNumber:  "UD-202",
  Title:        map[string]string{"ja": "クラブグラブ", "en": "CRAB GRAB"},
  ReleaseDate:  time.Date(1984, 2, 21, 0, 0, 0, 0, lc),
  Price:        6000,
  Series:       super,
  Million:      false,
  Premium:      "B",
  VariousColor: []string{"SILVER"},
  Genre:        "カニ退治",
  Character:    "ミスターグラブ",
}

bj60 = &GameWatch{
  No:           33,
  ModelNumber:  "BJ-60",
  Title:        map[string]string{"ja": "ブラックジャック", "en": "BLACK JACK"},
  ReleaseDate:  time.Date(1985, 2, 15, 0, 0, 0, 0, lc),
  Price:        6000,
  Series:       multi,
  Million:      false,
  Premium:      "C",
  VariousColor: []string{"RED"},
  Genre:        "トランプ",
  Character:    "トランプ",
}

}

views/App/Index.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 $value.Battery}}
              {{/* ネストしたrange */}}
              <ul>
              {{range $cell := $value.Battery}}
                <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

_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>

『 Go 』Article List
Category List

Eye Catch Image
Read More

Androidに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

AWSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Bitcoinに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

CentOSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

dockerに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

GitHubに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Goに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Javaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

JavaScriptに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Laravelに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Pythonに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Rubyに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Scalaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Swiftに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Unityに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Vue.jsに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Wordpressに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

機械学習に関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。