Kode program : Membuat template text

Golang Web – Membuat web dinamis dengan template

Sampai saat ini kita hanya membahas tentang membuat response menggunakan String dan juga static file. Pada kenyataan nya, saat kita membuat web kita pasti akan membuat halaman yang dinamis, bisa berubah-ubah sesuai data yang diakses oleh user. Di golang terdapat fitur HTML Template, yaitu fitur template yang bisa kita gunakan untuk membuat HTML yang dinamis.

HTML Template

Fitur HTML Template terdapat di package html/template, sebelum menggunakan template, kita perlu terlebih dahulu membuat template nya. Template bisa berupa file atau string. Bagian dinamis pada HTML Template adalah bagian yang menggunakan tanda {{ }}

Membuat HTML Template

Saat membuat template dengan string kita perlu memberitahu nama template nya, jika menggunakan file maka secara otomatis nama file nya akan digunakan sebagai nama template. Dan untuk membuat text template, cukup buat text html, dan untuk konten yang dinamis kita bisa gunakan tanda {{.}}, contoh : <html><body>{{.}}</body></html>

package belajar_golang_web

import (
	"fmt"
	"html/template"
	"io"
	"net/http"
	"net/http/httptest"
	"testing"
)

func SimpleHtml(writer http.ResponseWriter, request *http.Request) {
	templateText := `<html><body>{{.}}</body></html>`
	t := template.Must(template.New("SIMPLE").Parse(templateText))
	t.ExecuteTemplate(writer, "SIMPLE", "Hello HTML Template")
}

func TestSimpleHTML(t *testing.T) {
	request := httptest.NewRequest(http.MethodGet, "http://localhost:8080", nil)
	recorder := httptest.NewRecorder()

	SimpleHtml(recorder, request)

	body, _ := io.ReadAll(recorder.Result().Body)
	fmt.Println(string(body))
}
Kode program : Membuat template text
Kode program : Membuat template text

Template dari file

Selain membuat template dari string, kita juga bisa membuat template langsung dari file, hal ini mempermudah kita karena bisa langsung membuat file html. Saat membuat template menggunakan file, secara otomatis nama file akan menjadi nama templatenya, misal jika kita punya file simple.html, maka nama templatenya adalah simple.html. Dan di golang biasanya kita menggunakan ekstensi .gohtml.

package belajar_golang_web

import (
	"fmt"
	"html/template"
	"io"
	"net/http"
	"net/http/httptest"
	"testing"
)

func SimpleHtmlFile(writer http.ResponseWriter, request *http.Request) {
	t := template.Must(template.ParseFiles("./templates/simple.gohtml"))
	t.ExecuteTemplate(writer, "simple.gohtml", "Hello HTML Template")
}

func TestSimpleHTMLFile(t *testing.T) {
	request := httptest.NewRequest(http.MethodGet, "http://localhost:8080", nil)
	recorder := httptest.NewRecorder()

	SimpleHtmlFile(recorder, request)

	body, _ := io.ReadAll(recorder.Result().Body)
	fmt.Println(string(body))
}
Kode program : Membuat template file
Kode program : Membuat template file

Template directory

Kadang jarang sekali kita menyebutkan file template satu persatu. Alangkah baiknya untuk template kita simpan di satu directory. Golang template mendukung proses load template dari directory. Hal ini memudahkan kita, sehingga tidak perlu meneyebutkan nama file nya satu persatu.

package belajar_golang_web

import (
	"fmt"
	"html/template"
	"io"
	"net/http"
	"net/http/httptest"
	"testing"
)

func TemplateDirectory(writer http.ResponseWriter, request *http.Request) {
	t := template.Must(template.ParseGlob("./templates/*.gohtml"))
	t.ExecuteTemplate(writer, "simple.gohtml", "Hello HTML Template")
}

func TestTemplateDirectory(t *testing.T) {
	request := httptest.NewRequest(http.MethodGet, "http://localhost:8080", nil)
	recorder := httptest.NewRecorder()

	TemplateDirectory(recorder, request)

	body, _ := io.ReadAll(recorder.Result().Body)
	fmt.Println(string(body))
}
Kode program : Membuat template directory
Kode program : Membuat template directory

Template dari golang embed

Karena saat ini sudah ada golang embed, jadi di rekomendasikan menggunakan golang embed untuk menyimpan data template. Menggunakan golang embed menjadikan kita tidak perlu meng-copy template file lagi, karena sudah di embed di dalam distribution file.

package belajar_golang_web

import (
	"embed"
	"fmt"
	"html/template"
	"io"
	"net/http"
	"net/http/httptest"
	"testing"
)

//go:embed templates/*.gohtml
var templates embed.FS

func TemplateEmbed(writer http.ResponseWriter, request *http.Request) {
	t := template.Must(template.ParseFS(templates, "templates/*.gohtml"))
	t.ExecuteTemplate(writer, "simple.gohtml", "Hello HTML Template")
}

func TestTemplateEmbed(t *testing.T) {
	request := httptest.NewRequest(http.MethodGet, "http://localhost:8080", nil)
	recorder := httptest.NewRecorder()

	TemplateEmbed(recorder, request)

	body, _ := io.ReadAll(recorder.Result().Body)
	fmt.Println(string(body))
}
Kode program : Membuat template embed
Kode program : Membuat template embed

Template Data

Saat kita membuat template, kadang kita ingin menambahkan banyak data dinamis. Hal ini bisa kita lakukan dengan cara menggunakan data struct atau map. Namun perlu dilakukan perubahan di dalam text templatenya, kita perlu memberi tahu Field atau Key mana yang akan kita gunakan untuk mengisi data dinamis di template. Contohnya seperti {{.NamaField}}

Buatlah file baru di dalam templates dan beri nama name.gohtml kemudian isikan baris kode berikut

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{.Title}}</title>
</head>
<body>
    <h1>Hello {{.Name}}</h1>
</body>
</html>
Template Data
Template Data

Setelah membuat template, selanjutnya buta lagi file baru pada root project dengan nama template_data_test.go kemudian masukkan baris kode berikut

package belajar_golang_web

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"testing"
	"text/template"
)

func TemplateDataMap(writer http.ResponseWriter, request *http.Request) {
	t := template.Must(template.ParseFiles("./templates/name.gohtml"))
	t.ExecuteTemplate(writer, "name.gohtml", map[string]interface{}{
		"Title": "Template Data Map",
		"Name":  "Rendy",
	})
}

func TestTemplateDataMap(t *testing.T) {
	request := httptest.NewRequest(http.MethodGet, "http://localhost:8080", nil)
	recorder := httptest.NewRecorder()

	TemplateDataMap(recorder, request)

	body, _ := io.ReadAll(recorder.Result().Body)
	fmt.Println(string(body))
}
Kode program : Template data map
Kode program : Template data map

Pada percobaan diatas kita mencoma membutat template data dengan map, selanjutnya kita akan mencoba membuat template data dengan struct

package belajar_golang_web

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"testing"
	"text/template"
)

type Page struct {
	Title, Name string
}

func TemplateDataStruct(writer http.ResponseWriter, request *http.Request) {
	t := template.Must(template.ParseFiles("./templates/name.gohtml"))
	t.ExecuteTemplate(writer, "name.gohtml", Page{
		Title: "Template Data Struct",
		Name:  "Rendy",
	})
}

func TestTemplateDataStruct(t *testing.T) {
	request := httptest.NewRequest(http.MethodGet, "http://localhost:8080", nil)
	recorder := httptest.NewRecorder()

	TemplateDataStruct(recorder, request)

	body, _ := io.ReadAll(recorder.Result().Body)
	fmt.Println(string(body))
}
Kode program : Template data struct
Kode program : Template data struct

Apabila anda memiliki kebutuhan untuk mengirim nested data, maka pada bagian template anda dapat menggunakan {{.NamaField.NestedField}} contoh nya seperti tag h2 berikut

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{.Title}}</title>
</head>
<body>
    <h1>Hello {{.Name}}</h1>
    <h2>{{.Address.Street}}</h2>
</body>
</html>

Kemudian pada bagian kode program nya cukup buat nested struct atau map seperti biasanya

package belajar_golang_web

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"testing"
	"text/template"
)

type Address struct {
	Street string
}

type Page struct {
	Title, Name string
	Address     Address
}

func TemplateDataStruct(writer http.ResponseWriter, request *http.Request) {
	t := template.Must(template.ParseFiles("./templates/name.gohtml"))
	t.ExecuteTemplate(writer, "name.gohtml", Page{
		Title: "Template Data Struct",
		Name:  "Rendy",
		Address: Address{
			Street: "JL. Belum Ada",
		},
	})
}

func TestTemplateDataStruct(t *testing.T) {
	request := httptest.NewRequest(http.MethodGet, "http://localhost:8080", nil)
	recorder := httptest.NewRecorder()

	TemplateDataStruct(recorder, request)

	body, _ := io.ReadAll(recorder.Result().Body)
	fmt.Println(string(body))
}

func TemplateDataMap(writer http.ResponseWriter, request *http.Request) {
	t := template.Must(template.ParseFiles("./templates/name.gohtml"))
	t.ExecuteTemplate(writer, "name.gohtml", map[string]interface{}{
		"Title": "Template Data Map",
		"Name":  "Rendy",
		"Address": map[string]interface{}{
			"Street": "JL. Belum Ada",
		},
	})
}

func TestTemplateDataMap(t *testing.T) {
	request := httptest.NewRequest(http.MethodGet, "http://localhost:8080", nil)
	recorder := httptest.NewRecorder()

	TemplateDataMap(recorder, request)

	body, _ := io.ReadAll(recorder.Result().Body)
	fmt.Println(string(body))
}
Kode program : Template data nested map & struct
Kode program : Template data nested map & struct

Template Action

Golang template sebenarnya mendukung perintah action, seperti percabangan, perulangan, dan lain-lain.

If else

  • {{if.Value}} T1 {{end}}, jika Value tidak kosong, maka T1 akan di eksekusi, jika kosong, tidak ada yang dieksekusi
  • {{if.Value}} T1 {{else}} T2 {{end}}, jika Value tidak kosong maka T1 akan di eksekusi, jika kosong maka T2 yang akan di eksekusi
  • {{if.Value1}} T1 {{else if.Value2}} T2 {{else}} T3 {{end}}, jika Value1 tidak kosong maka T1 akan di eksekusi, jika Value2 tidak kosong maka T2 akan di eksekusi, jika tidak semua maka T3 akan di eksekusi.

Buatlah file template baru dan masukkan baris kode berikut

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{.Title}}</title>
</head>
<body>
{{if .Name}}
    <h1>Hello {{.Name}}</h1>
{{else}}
    <h1>Hello</h1>
{{end}}
</body>
</html>
Template action if
Template action if

Selanjutnya buat file baru di root project dengan nama template_action_test.go dan masukkan baris kode berikut

package belajar_golang_web

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"testing"
	"text/template"
)

func TemplateActionIf(writer http.ResponseWriter, request *http.Request) {
	t := template.Must(template.ParseFiles("./templates/if.gohtml"))
	t.ExecuteTemplate(writer, "if.gohtml", Page{
		Title: "Template Data Struct",
		Name:  "Rendy",
	})
}

func TestTemplateActionIf(t *testing.T) {
	request := httptest.NewRequest(http.MethodGet, "http://localhost:8080", nil)
	recorder := httptest.NewRecorder()

	TemplateActionIf(recorder, request)

	body, _ := io.ReadAll(recorder.Result().Body)
	fmt.Println(string(body))
}
Kode program : Template action if statement
Kode program : Template action if statement

Pada kode program diatas, jika kita mengirim data Name pada struct maka template akan mengeksekusi kode <h1>Hello {{.Name}}</h1> jika tidak maka yang akan di eksekusi adalah <h1>Hello</h1>

Operator perbandingan

Golang template juga mendukung operator perbandingan, ini cocok ketika butuh melakukan perbandingan number di if statement, berikut adalah operator nya

OperatorKeterangan
eqarg1 == arg2
nearg1 != arg2
ltarg1 < arg2
learg1 <= arg2
gtarg1 > arg1
gearg2 >= arg2
Operator golang template

Buatlah file template baru dengan nama comparator.gohtml dan isikan baris kode berikut

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{.Title}}</title>
</head>
<body>
{{if ge .FinalValue 80}}
    <h1>Good</h1>
{{else if ge .FinalValue 60}}
    <h1>Nice Try</h1>
{{else}}
    <h1>Try again</h1>
{{end}}
</body>
</html>
Template comparator
Template comparator

Jika di perhatikan, operator nya berada di depan, kenapa hal ini bisa terjadi? Hal ini dikarenakan, sebenarnya operator perbandingan tersebut adalah sebuah function. Jadi saat kita menggunakan {{eq First Second}}, sebenarnya dia akan memanggil function eq dengan parameter First dan Second : eq(First, Second).

package belajar_golang_web

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"testing"
	"text/template"
)

func TemplateActionOperator(writer http.ResponseWriter, request *http.Request) {
	t := template.Must(template.ParseFiles("./templates/comparator.gohtml"))
	t.ExecuteTemplate(writer, "comparator.gohtml", map[string]interface{}{
		"FinalValue": 90,
	})
}

func TestTemplateActionOperator(t *testing.T) {
	request := httptest.NewRequest(http.MethodGet, "http://localhost:8080", nil)
	recorder := httptest.NewRecorder()

	TemplateActionOperator(recorder, request)

	body, _ := io.ReadAll(recorder.Result().Body)
	fmt.Println(string(body))
}
Kode program : Template action comparator
Kode program : Template action comparator

Template action range

Range digunakan untuk iterasi data template, di golang template tidak ada perulangan seperti biasa, contohnya menggunakan for itu tidak bisa dilakukan pada golang template. Yang bisa kita lakukan adalah menggunakan range untuk megiterasi tiap data array, slice, map atau channel. Contohnya seperti berikut

{{range $index, $element := .Value}} T1 {{end}}, jika Value memiliki data, maka T1 akan di eksekusi sebanyak element value, dan kita bisa menggunakan $index untuk mengakses index dan $element untuk mengakses element.

{{range $index, $element := .Value}} T1 {{else}} T2 {{end}}, sama seperti sebelumnya, namun jika Value tidak memiliki element apapun, maka T2 akan di eksekusi.

Buatlah template baru dengan nama range.gohtml dan masukkan baris kode di bawah ini

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{.Title}}</title>
</head>
<body>
<h1>Daftar hobi :</h1>
{{range $index, $element := .Hobbies}}
    <h2>{{$element}}</h2>
{{else}}
    <h1>Anda tidak punya hobi</h1>
{{end}}
</body>
</html>
Template action range
Template action range

Jika sudah dibuat template nya, selanjutnya seperti biasa kita akan membuat handler dan unit test nya.

package belajar_golang_web

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"testing"
	"text/template"
)

func TemplateActionRange(writer http.ResponseWriter, request *http.Request) {
	t := template.Must(template.ParseFiles("./templates/range.gohtml"))
	t.ExecuteTemplate(writer, "range.gohtml", map[string]interface{}{
		"Title": "Template action range",
		"Hobbies": []string{
			"Game", "Read", "Code",
		},
	})
}

func TestTemplateActionRange(t *testing.T) {
	request := httptest.NewRequest(http.MethodGet, "http://localhost:8080", nil)
	recorder := httptest.NewRecorder()

	TemplateActionRange(recorder, request)

	body, _ := io.ReadAll(recorder.Result().Body)
	fmt.Println(string(body))
}
Kode program : Template action range
Kode program : Template action range

Template action with

Kadang kita sering menggunakan nested struct, jika menggunakan template kita bisa mengakses nya menggunakan .Value.NestedValue.

Di template terdapat action with, yang bisa digunakan mengubah scope dot menjadi object yang kita mau. Contohnya :

  • {{with .Value}} T1 {{end}}, jika value tidak kosong, di T1 semua dot akan merefer ke value.
  • {{with .Value}} T1 {{else}} T2 {{end}}, sama seperti sebelumnya, namun jika value kosong, maka T2 yang akan di eksekusi.

Untuk mencobanya, buatlah file template baru dengan nama address.gohtml dan seperti biasa masukkan baris kode berikut

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{.Title}}</title>
</head>
<body>
<h1>Name : {{.Name}}</h1>
{{with .Address}}
    <h1>Address Street : {{.Street}}</h1>
    <h1>Address City : {{.City}}</h1>
{{end}}
</body>
</html>
Template action with
Template action with

Setelah membuat template, selanjutnya buat handler dan unit test dengan baris kode berikut

package belajar_golang_web

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"testing"
	"text/template"
)

func TemplateActionWith(writer http.ResponseWriter, request *http.Request) {
	t := template.Must(template.ParseFiles("./templates/address.gohtml"))
	t.ExecuteTemplate(writer, "address.gohtml", map[string]interface{}{
		"Title": "Template action with",
		"Name":  "Rendy",
		"Address": map[string]interface{}{
			"Street": "JL. Belum Ada",
			"City":   "Malang",
		},
	})
}

func TestTemplateActionWith(t *testing.T) {
	request := httptest.NewRequest(http.MethodGet, "http://localhost:8080", nil)
	recorder := httptest.NewRecorder()

	TemplateActionWith(recorder, request)

	body, _ := io.ReadAll(recorder.Result().Body)
	fmt.Println(string(body))
}
Kode program : Template action with
Kode program : Template action with

Comment

Template juga mendukung komentar, komentar secara otomatis akan hilang ketika template text di parsing. Untuk membuat komentarsangat sederhana, kita bisa gunakan {{/* Contoh komentar */}}.

Template Layout

Saat membuat halaman website, kadang ada beberapa bagian yang selalu sama, misal header dan footer. Best practice nya jika terdapat bagian yang selalu sama disarankan utnuk disimpan pada template terpisah, agar bisa digunakan di template lain. Untuk melakukan import template kita bisa gunakan seperti berikut :

  • {{template "nama"}}, artinya kita akan meng-import template “nama” tanpa memberikan data apapun
  • {{template "nama" .Value}}, artinya kita akan meng-import template “nama” dengan memberikan data Value

Contohnya kita akan memisahkan antara header, body dan footer menjadi template terpisah, karena header dan footer biasanya selalu sama dan tidak berubah-ubah. Jadi kita hanya perlu fokus pada body yang bisa berubah ubah isi konten nya.

Pertama buatlah file template baru yaitu header.gohtml dan isikan baris kode berikut

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{.Title}}</title>
</head>
<body>

Selanjutnya buat juga file template untuk footer footer.gohtml dan isinya cukup seperti berikut

</body>
</html>

Kemudian untuk bagian konten nya, buatlah file template baru dengan nama layout.gohtml dan isinya seperti berikut

{{template "header.gohtml" .}}
<h1>Hello {{.Name}}</h1>
{{template "footer.gohtml"}}
Template layout
Template layout

Perhatikan pada baris ke 1 kita melakukan import template header.gohtml dan mengirimkan semua data ke template tersebut, oleh karena itu kita menggunakan . dan untuk template footer.gohtml kita tidak butuh mengirim data apapun, jadi kita tidak menggunakan . atau .Value

Selanjutnya untuk kode program template layout nya, buat lah file baru pada root project terlebih dahulu dengan nama template_layout_test.go dan isikan baris kode berikut

package belajar_golang_web

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"testing"
	"text/template"
)

func TemplateLayout(writer http.ResponseWriter, request *http.Request) {
	t := template.Must(template.ParseFiles(
		"./templates/header.gohtml",
		"./templates/footer.gohtml",
		"./templates/layout.gohtml",
	))
	t.ExecuteTemplate(writer, "layout.gohtml", map[string]interface{}{
		"Title": "Template layout",
		"Name":  "Rendy",
	})
}

func TestTemplateLayout(t *testing.T) {
	request := httptest.NewRequest(http.MethodGet, "http://localhost:8080", nil)
	recorder := httptest.NewRecorder()

	TemplateLayout(recorder, request)

	body, _ := io.ReadAll(recorder.Result().Body)
	fmt.Println(string(body))
}
Kode program : Template layout
Kode program : Template layout

Saat menggunakan template layout, kita harus mengimport semua layout nya, seperti pada baris 13. Pada kenyataannya nanti kita tidak akan menggunakan ParseFiles karena harus menuliskan semua layout nya, biasanya kita akan menggunkaan glob atau embed seperti yang sudah di bahas sebelumnya.

Template name

Saat kita membuat template dari file, secara otomatis nama file nya akan menjadi nama template. Namun jika kita ingin mengubah nama template nya, kita juga bisa melakukannya dengan menggunakan perintah {{define "nama"}} TEMPLATE {{end}}, artinya kita membuat template dengan nama “nama”.

Contohnya seperti berikut, buka kembali file layout.gohtml dan ubah menjadi

{{define "layout"}}
{{template "header.gohtml" .}}
<h1>Hello {{.Name}}</h1>
{{template "footer.gohtml"}}
{{end}}
Template name
Template name

Kemudian untuk pemanggilan template nya kita tidak perlu menuliskan full seperti layout.gohtml tapi hanya perlu layout saja seperti berikut.

package belajar_golang_web

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"testing"
	"text/template"
)

func TemplateLayout(writer http.ResponseWriter, request *http.Request) {
	t := template.Must(template.ParseFiles(
		"./templates/header.gohtml",
		"./templates/footer.gohtml",
		"./templates/layout.gohtml",
	))
	t.ExecuteTemplate(writer, "layout", map[string]interface{}{
		"Title": "Template layout",
		"Name":  "Rendy",
	})
}

func TestTemplateLayout(t *testing.T) {
	request := httptest.NewRequest(http.MethodGet, "http://localhost:8080", nil)
	recorder := httptest.NewRecorder()

	TemplateLayout(recorder, request)

	body, _ := io.ReadAll(recorder.Result().Body)
	fmt.Println(string(body))
}
Kode program : Template name
Kode program : Template name

Template function

Selain mengakses field dalam template , function juga bisa diakses, contohnya jika kita memiliki function di dalam struct. Cara mengakses function sama seperti field, namun jika function tersebut memiliki parameter kita bisa tambahkan parameter ketika memanggil function di templatenya. Contoh :

  • {{.FunctionName}}, memanggil filed FunctionName atau function FunctionName()
  • {{.FunctionName "Rendy" "Wijaya"}}, memanggil function FunctionName("Rendy", "Wijaya")

Untuk mencoba nya, buatlah file baru dengan nama template_function_test.go dan masukkan baris kode berikut

package belajar_golang_web

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"testing"
	"text/template"
)

type MyPage struct {
	Name string
}

func (mypage MyPage) SayHello(name string) string {
	return "Hello " + name + ", My name is " + mypage.Name
}

func TemplateFunction(writer http.ResponseWriter, request *http.Request) {
	t := template.Must(template.New("FUNCTION").Parse(`{{.SayHello "Budi"}}`))
	t.ExecuteTemplate(writer, "FUNCTION", MyPage{
		Name: "Rendy",
	})
}

func TestTemplateFunction(t *testing.T) {
	request := httptest.NewRequest(http.MethodGet, "http://localhost:8080", nil)
	recorder := httptest.NewRecorder()

	TemplateFunction(recorder, request)

	body, _ := io.ReadAll(recorder.Result().Body)
	fmt.Println(string(body))
}
Kode program : Template function
Kode program : Template function

Golang template global function

Golang template memiliki beberapa global function, global function adalah function yang bisa digunakan secara langsung, tanpa menggunakan template data. Berikut adalah beberapa global function di golang template : https://github.com/golang/go/blob/master/src/text/template/funcs.go

Selain global function bawaan golang, kita juga bisa menambahkan global function buatan kita sendiri. Untuk menambah global function, kita bisa menggunakan method Funcs pada template. Tetapi perlu di ingat, bahwa menambahkan global function harus dilakukan sebelum parsing template.

package belajar_golang_web

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"text/template"
)

func TemplateCreateGLobal(writer http.ResponseWriter, request *http.Request) {
	t := template.New("FUNCTION")
	t = t.Funcs(map[string]interface{}{
		"upper": func(value string) string {
			return strings.ToUpper(value)
		},
	})
	t = template.Must(t.Parse(`{{upper .Name}}`))

	t.ExecuteTemplate(writer, "FUNCTION", MyPage{
		Name: "Rendy",
	})
}

func TestTemplateCreateGlobal(t *testing.T) {
	request := httptest.NewRequest(http.MethodGet, "http://localhost:8080", nil)
	recorder := httptest.NewRecorder()

	TemplateCreateGLobal(recorder, request)

	body, _ := io.ReadAll(recorder.Result().Body)
	fmt.Println(string(body))
}
Kode program : Template create global function
Kode program : Template create global function

Golang template function piplines

Golang template mendukung function piplines, artinya hasil dari function bisa dikirim ke function berikutnya. Untuk menggunakan function pipelines, kita bisa menggunakan tanda |, misal : {{ sayHello .Name | upper}}, artinya akan memanggil global function sayHello(Name) hasil dari sayHello(name) akan dikirim ke function upper(hasil). Dan di golang template kita bisa menambahkan lebih dari satu function pipelines.

package belajar_golang_web

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"text/template"
)

func TemplateCreateGLobalPipelines(writer http.ResponseWriter, request *http.Request) {
	t := template.New("FUNCTION")
	t = t.Funcs(map[string]interface{}{
		"sayHello": func(name string) string {
			return "Hello " + name
		},
		"upper": func(value string) string {
			return strings.ToUpper(value)
		},
	})
	t = template.Must(t.Parse(`{{sayHello .Name | upper}}`))

	t.ExecuteTemplate(writer, "FUNCTION", MyPage{
		Name: "Rendy",
	})
}

func TestTemplateCreateGlobalPipelines(t *testing.T) {
	request := httptest.NewRequest(http.MethodGet, "http://localhost:8080", nil)
	recorder := httptest.NewRecorder()

	TemplateCreateGLobalPipelines(recorder, request)

	body, _ := io.ReadAll(recorder.Result().Body)
	fmt.Println(string(body))
}
Kode program : Template create global pipelines
Kode program : Template create global pipelines

Template parshing

Kode-kode diatas yang sudah kita praktekan sebenarnya tidak efisien, hal ini dikarenakan setiap handler dipanggil, kita selalu melakukan parsing ulang templatenya. Idealnya template hanya melakukan parsing satu kali diawali ketika aplikasinya berjalan. Selanjutnya data template akan di caching(disimpan di memory), sehingga kita tidak perlu melakukan parsing lagi. Hal ini akan membuat web kita semakin cepat.

package belajar_golang_web

import (
	"embed"
	"fmt"
	"html/template"
	"io"
	"net/http"
	"net/http/httptest"
	"testing"
)

//go:embed templates/*.gohtml
var templates embed.FS

var myTemplates = template.Must(template.ParseFS(templates, "templates/*.gohtml"))

func TemplateCaching(writer http.ResponseWriter, request *http.Request) {
	myTemplates.ExecuteTemplate(writer, "simple.gohtml", "Hello Template Caching")
}

func TestTemplateCaching(t *testing.T) {
	request := httptest.NewRequest(http.MethodGet, "localhost:8080", nil)
	recorder := httptest.NewRecorder()

	TemplateCaching(recorder, request)

	body, _ := io.ReadAll(recorder.Result().Body)
	fmt.Println(string(body))
}
Kode program : Template caching
Kode program : Template caching

Sebenar nya jika kita eksekusi kode program nya maka hasilnya akan sama saja, yang membedakan adalah karena kita melakukan parsing template nya di global maka, parsing tersebut akan dijalankan hanya satu kali saat aplikasi pertama berjalan dan hasilnya akan di simpan di memory. Dengan demikian saat handler dijalankan kita tidak perlu parsing kembali karena sudah tersimpan di memory dan tinggal di pakai saja.

Penutup

Pada artikel kali ini kita telah belajar tentang template pada golang web. Dan pada artikel selanjutnya saya akan membahas XSS Cross Site Scripting pada golang web.

Leave a reply:

Your email address will not be published.

Site Footer

Sliding Sidebar

About Me

About Me

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam.

Social Profiles

Facebook