Go text and HTML templates

Template packages

Server side languages offers a mechanism for inserting a dynamically generated content into static pages. Go provides a mechanism for substituting the value of variables into text or HTML template. 

A Go template is string or file containing text (HTML) and actions, that are string expressions enclosed in double braces {{ ... }} that trigger some behaviors. Actions are powerful notations for selecting and printing fields, expressing control flow and calling functions.

Go standard library offers two package for managing templates:
The packages share the same interface so the following examples that treat HTML can also be applied to simple text.
  • text/template, implements data-driven templates for generating textual output
  • html/template, is the same as text/template but automatically secures HTML output against certain attacks (script injection)
Gin-Gonic Gin, the fast and full-featured web framework for Go that we have already seen in the GIF API post, supports standard template library, so we adopt this framework in our sample application.

Product catalog template


We start creating an HTML file UTF-8-encoded which will contain the product catalog to be printed. We want to see a list of products, then we should do a loop on our data structure.
We will use the action {{range}} to produce the loop, print data in the struct Product and display styles alternating conditionally.

Here is the template file mytemplate.html which we will load the next program.


<html>
<head>
    <meta charset="UTF-8">
    <title>{{.Title}}</title>
</head>
<body>
<div>
<h1>
    {{ .Title }}
</h1>
</div>
<div>
    {{$layout := "Mon Jan _2 2006"}}
    {{$BestProductId := 2}}
    {{range .Products}}
    {{with .Style}}
        <ul style="font-family:{{.Font}};color:{{.Color}};">
    {{end}}
    {{if eq .Id $BestProductId }}
            <li style="color:blue"><b> Best Product: {{.Name}}</b></li>
    {{else}}
            <li style="color:blue"> Product: {{.Name}}</li>
    {{end}}
            <li> Note: {{.Note}}</li>
            <li> Category: {{.Category}}</li>
            {{if .Style.Border}}
            <li style="border-bottom:1px solid"> Update Date: 
                        {{.UpdateDate.Format $layout }}</li>
            {{else}}
            <li> Update Date: {{.UpdateDate.Format $layout }}</li>
            {{end}}
        </ul>
    {{end}}
</div>
</body>

</html>


Parsing and evaluating template


Templates are executed by applying them to a data structure. The output of the template is produced by a two-step process:
  1. the template is parsed producing an internal representation of the structures and the expressions 
  2. the template is executed to produce the output
Parsing need to be done only once, than a template may be executed safely in parallel.

The function template.ParseFiles("filename") read the file and produce the inner representation of the template. It can be used to read more than one file if these make up the template.
The function template.Must(template) checks a template and panic if it's invalid.

Let's see how to use them in a simple web server (thanks to Gin).


package main

import (
    "github.com/gin-gonic/gin"
    "html/template"
    "net/http"
    "strconv"
    "time"
)

type Product struct {
    Id         int
    Name       string
    Note       string
    Category   int
    UpdateDate time.Time
    Style      Style
}

type Style struct {
    Color  string
    Font   string
    Bold   bool
    Border bool
}

func main() {
    router := gin.Default()
    html := template.Must(template.ParseFiles("mytemplate.html"))
    router.SetHTMLTemplate(html)
    router.GET("/index", RenderTemplate)
    router.Run(":8080")
}

func RenderTemplate(c *gin.Context) {
    c.HTML(http.StatusOK, "mytemplate.html", gin.H{
        "Title":    "Product Catalog",
        "Products": getProductList(),
    })
}

func getProductList() *[]Product {
    list := make([]Product, 8, 8)
    for i := 0; i < 8; i++ {
        list[i] = Product{Id: i, Name: "Product N." + strconv.Itoa(i), Note: "new product", Category: i*10 + 100, UpdateDate: time.Now()}
        if i%2 == 0 {
            list[i].Style.Color = "green"
            list[i].Style.Font = "Arial"
            list[i].Style.Bold = true
            list[i].Style.Border = true
        } else {
            list[i].Style.Color = "red"
            list[i].Style.Font = "Arial"
            list[i].Style.Bold = true
            list[i].Style.Border = true
        }
    }
    return &list
}



The program creates a list of Product structs and populate it with example data. The list is passed to the template calling the gin.Context.HTML helper function in witch we specify the template name and then we use the helper gin.H function to construct a map of values applied in the template.

Watching the template we can examine the actions used to produce the output and see how they interact with the data structure.


Actions

These are the actions used in the example. The complete list of available actions is defined in the official Go documentation (package template). 
The actions operate on "pipelines" that are evaluations of data. 

{{ range pipeline }}

This action allows us to iterate through the elements of a list. Looping on the list of products we can print the catalog.

{{ with pipeline }}

This action allows us to access elements of the structure more easily, without having to concatenate the fields and subfields with dot. We use it to access the Style associated with the Product.

{{ if pipeline }}

This is a flow control action, if the value of the pipeline is empty no output is generated. They are considered empty values false, 0, nil and zero length array, slice, map and string.

Other elements of the template


Pipeline

A pipeline may be "chained" by separating a sequence of commands with pipeline characters '|' where the result of the each command is passed as the last argument of the following command. For example {{ .Name | printf %s }} 


Variables

The variables can be defined in an action. The name of a variable must begin with character '$'. Variables can be printed, used for making comparisons, can be passed to functions.


Conclusions


The templates are simple and powerful, can be a viable alternative to other technologies such as PHP, JSP and ASP.NET.

There are interesting projects that make use of template and show remarkable performance, one is the aforementioned Gin and another very promising Hugo is also offering support for the Markdown language, including the GitHub version.


As usual, the sample code of the example can be downloaded from here https://github.com/maxzerbini/packagemain/tree/master/html/template


Commenti

  1. Definitely found the markdown export functionality in the INK for All text editor helpful

    RispondiElimina

Posta un commento

Post popolari in questo blog

OAuth 2.0 Server & Authorization Middleware for Gin-Gonic

From the database schema to RESTful API with DinGo