LoginSignup
0
0

【Go言語】テンプレートエンジンでtableをレンダリングしてechoで表示してみた

Last updated at Posted at 2024-05-10

はじめに

Goの標準で用意されているテンプレートエンジン(html/template)を使って、htmlのtableをレンダリングし、echoでwebサーバーを建てて表示してみました。

環境

  • macOS Sonoma 14.4.1
  • Go 1.22.3 darwin-arm64

表示したいもの

image.png

このようなテーブルを表示するhtmlを出力することがゴールとなります。

html
<!DOCTYPE html>
<html lang="ja">
<meta charset="utf-8">
<title>Table Rendering</title>
<link rel="stylesheet" href="/public/css/style.css" type="text/css">
<body>
<table>
    <thead>
        <tr>
            <th></th>
            <th>Left</th>
            <th>Center</th>
            <th>Right</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <th>Top</th>
            <td>0</td>
            <td>1</td>
            <td>2</td>
        </tr>
        <tr>
            <th>Center</th>
            <td>3</td>
            <td>4</td>
            <td>5</td>
        </tr>
        <tr>
            <th>Bottom</th>
            <td>6</td>
            <td>7</td>
            <td>8</td>
        </tr>
    </tbody>
</table>
</body>
</html>
/public/css/style.css
thead {
    background-color: #ffccff;  /* table headerの背景色指定 */
}

tbody {
    background-color: #ccffff;  /* table bodyの背景色指定 */
}

th,td {
    border: solid 1px;          /* 枠線指定 */
}

tableへのデータを渡す方法

テンプレートエンジンにデータを格納する際、
<tbody></tbody>において、どこで繰り返し処理をするか?を考えたとき、下記2通りのパターンを考えました。

パターン1

テンプレートは<tr></tr>までに留めておき、その間に1行ずつ<th></th>を1列+<td></td>を3列分のhtmlタグ3行分格納した2次元のスライスで渡します。すなわち、4列×3行のスライスになります。
<tbody></tbody>の中に渡したい構造体:<tr></tr>の間に[][]template.HTML

パターン2

テンプレートで<th></th><td></td>まで作って、その間に1次元スライスのstringデータを渡します。テンプレート側で<th></th>は1回、<td></td>は{{range}}により3回繰り返されます。
<tbody></tbody>の中に渡したい構造体:<th></th>の間にstring、<td></td>の間に[]string

実践

webサーバー立ち上げ

まず初めに、下記のコマンドでmoduleの初期化を行います。

go mod init SSR

次にmain.goを作成し、下記の通りechoでwebサーバーを建てます。

main.go
package main

import (
	"SSR/model"

	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
)

func main() {
	e := echo.New()
	e.Use(middleware.Logger())
	e.Use(middleware.Recover())
	e.Static("/public/css", "./public/css") //cssファイルのアドレス指定
	e.GET("/1", model.ShowTable1)    //パターン1
	e.GET("/2", model.ShowTable2)    //パターン2
	e.Logger.Fatal(e.Start(":8080")) //ポート8080でwebサーバー起動
}

データを渡すための構造体

まず構造体宣言のファイルを作成します。
Body_valの中身がパターン1及びパターン2の構造体に対応していて、<tr></tr>の間に渡されます。

/model/struct.go
package model

import "html/template"

/* パターン1 */
type Table1 struct {
	Column   []string
	Body_val []Body1
}

type Body1 struct {
	Array [][]template.HTML    //2次元スライス
}

/* パターン2 */
type Table2 struct {
	Column   []string
	Body_val []Body2
}

type Body2 struct {
	Indexdata string
	Rowdata   []string         //1次元スライス
}

テンプレートファイルの作成

構造体を流し込むためのテンプレートを下記の通り準備しました。
Body_valの中の構造体を渡すときに、{{range $s:=.Body_val}}として$sに置き換えておくことで、入れ子になっている構造体の中を取り出しやすくなります。
コツとして、{{range ***}}は繰り返したいタグの一つ上の行の最後に置き、
{{end}}は繰り返したいタグの直後に置くと変な改行が入らず、見やすいhtmlが出力されます。

パターン1

template1.html.tmpl
<!DOCTYPE html>
<html lang="ja">
<meta charset="utf-8">
<title>Table Rendering</title>
<link rel="stylesheet" href="/public/css/style.css" type="text/css">
<body>
<table>
    <thead>
        <tr>{{range .Column}}
            <th>{{.}}</th>{{end}}
        </tr>
    </thead>
    <tbody>{{range $s:=.Body_val}}{{range $s.Array}}
        <tr>{{ range .}}
            {{.}}{{end}}
        </tr>{{end}}{{end}}
    </tbody>
</table>
</body>
</html>

パターン2

template2.html.tmpl
<!DOCTYPE html>
<html lang="ja">
<meta charset="utf-8">
<title>Table Rendering</title>
<link rel="stylesheet" href="/public/css/style.css" type="text/css">
<body>
<table>
    <thead>
        <tr>{{range .Column}}
            <th>{{.}}</th>{{end}}
        </tr>
    </thead>
    <tbody>{{range $s:=.Body_val}}
        <tr>
            <th>{{$s.Indexdata}}</th>{{range $s.Rowdata}}
            <td>{{.}}</td>{{end}}
        </tr>{{end}}
    </tbody>
</table>
</body>
</html>

tableへのレンダリング

用意した構造体にデータを格納していきます。パターン1とパターン2でBody_valへの格納の仕方を見比べて見てください。

/model/showtable.go
package model

import (
	"bytes"
	"html/template"
	"io"
	"log"
	"net/http"
	"strconv"

	"github.com/labstack/echo/v4"
)

/* パターン1 */
func ShowTable1(c echo.Context) error {
	var buffer bytes.Buffer     //一旦バッファーにhtmlを出力する
	tpl := template.Must(template.ParseFiles("template1.html.tmpl"))
	data := Table1{}
	data.Column = []string{"", "Left", "Center", "Right"}    //headerのカラム
	body_temp := Body1{}
	row := []string{"Top", "Center", "Bottom"}
	for i := 0; i < 3; i++ {
		htmlstring := []template.HTML{}
		htmlstring = append(htmlstring, template.HTML(`<th>`+row[i]+`</th>`))
		for j := 0; j < 3; j++ {
			numstring := strconv.Itoa(i*3 + j)
			htmlstring = append(htmlstring, template.HTML(`<td>`+numstring+`</td>`))
		}
		body_temp.Array = append(body_temp.Array, htmlstring)    //htmlタグを格納した4列×3行の2次元スライスを渡す
	}
	data.Body_val = append(data.Body_val, body_temp)
	err := tpl.Execute(&buffer, data)
	if err != nil {
		log.Print(err)
	}
	return c.HTML(http.StatusOK, buffer.String())
}

/* パターン2 */
func ShowTable2(c echo.Context) error {
	var buffer bytes.Buffer     //一旦バッファーにhtmlを出力する
	tpl := template.Must(template.ParseFiles("template2.html.tmpl"))
	data := Table2{}
	data.Column = []string{"", "Left", "Center", "Right"}    //headerのカラム
	row := []string{"Top", "Center", "Bottom"}
	for i := 0; i < 3; i++ {
		rowdata := Body2{}
		rowdata.Indexdata = row[i]
		for j := 0; j < 3; j++ {
			numstring := strconv.Itoa(i*3 + j)
			rowdata.Rowdata = append(rowdata.Rowdata, numstring)
		}
		data.Body_val = append(data.Body_val, rowdata)    //行ごとのデータを格納した1次元スライスを渡す
	}
	err := tpl.Execute(&buffer, data)
	if err != nil {
		log.Print(err)
	}
	return c.HTML(http.StatusOK, buffer.String())
}

動作確認

go mod tidy
go run main.go

とコマンドを打ち込んでwebサーバーを起動し、動作確認します。
ブラウザで下記URLを打ち込んで、上記テーブルが表示されれば完成です。

パターン1

http://localhost:8080/1

パターン2

http://localhost:8080/2

結果再掲
image.png

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0