Learn to build and deploy simple Go Web Apps, Part Three

In part one of this tutorial, we installed the Google App Engine SDK and then built and ran a simple web app locally. In part two, we deployed this simple web app to the Google App Engine and shared it with users worldwide.

Now, we will learn about the template package (namely text/template and html/template).

text/template

Usage: import "text/template"

Most server-side languages have a mechanism for taking predominantly static pages and inserting a dynamically generated component, such as a list of items. Typical examples are scripts in Java Server Pages, PHP scripting and many others. Go has adopted a relatively simple scripting language in the template package.

The package is designed to take text as input and output different text, based on transforming the original text using the values of an object.

To generate HTML output, we shall soon use package html/template, which has the same interface as this package but automatically secures HTML output against certain attacks.

The original source is called a template and will consist of text that is transmitted unchanged, and embedded commands which can act on and change text. The commands are delimited by {{ ... }}, similar to the JSP commands and PHPs.

A template is applied to a Go object. Fields from that Go object can be inserted into the template, and you can “dig” into the object to find sub-fields, etc. The current object is represented as ., so that to insert the value of the current object as a string, you use {{.}}. The package uses the fmt package by default to work out the string used as inserted values.

To insert the value of a field of the current object, you use the field name prefixed by .. For example, if the object is of type:

type Student struct {
        Name string
}

then you insert the values of Name by The name is {{.Name}}.

Thus, templates are a way to merge generic text with more specific text i.e. retain the content that is common in the template and then substitute the specific content as required.

The syntax of such definitions is to surround each template declaration with a “define” and “end” action.

The define action names the template being created by providing a string constant. Here is a simple example:

`{{define "T1"}}ONE{{end}}
{{define "T2"}}TWO{{end}}
{{define "T3"}}{{template "T1"}} {{template "T2"}}{{end}}
{{template "T3"}}`

This defines two templates, T1 and T2, and a third T3 that invokes the other two when it is executed. Finally it invokes T3. If executed this template will produce the text

ONE TWO

In Go, we use the template package and methods like Parse, ParseFile, Execute to load a template from a string or file and then perform the merge. The content to merge is within a defined type and that has exported fields, i.e. fields within the struct that are used within the template have to start with a capital letter.

Let us look at a simple example.

Make a new folder and cd to it as follows:

$ mkdir $GOPATH/src/github.com/SatishTalim/texttmpl
$ cd $GOPATH/src/github.com/SatishTalim/texttmpl

In this folder write the program stud_struct.go as follows:

package main

import (
        "os"
        "text/template"
)

type Student struct {
        //exported field since it begins
        //with a capital letter
        Name string
}

func main() {
        //define an instance
        s := Student{"Satish"}

        //create a new template with some name
        tmpl := template.New("test")

        //parse some content and generate a template
        tmpl, err := tmpl.Parse("Hello {{.Name}}!")

        //A common use of panic is to abort if a function
        //returns an error value that we don't know how to
        //(or want to) handle.
        if err != nil {
                panic(err)
        }

        //merge template 'tmpl' with content of 's'
        err1 := tmpl.Execute(os.Stdout, s)

        if err1 != nil {
                panic(err1)
        }
}

You can now run the program by typing:

$ go run stud_struct.go

The output is:

Hello Satish!

Note:

  • New allocates a new template with the given name.
  • Parse parses a string into a template.
  • To include the content of a field within a template, enclose it within curly braces and add a dot at the beginning. E.g. if Name is a field within a struct and its value needs to be substituted while merging, then include the text {{.Name}} in the template. Do remember that the field name has to be present and it should also be exported (i.e. it should begin with a capital letter in the type definition), or there could be errors. All text outside {{.Name}} is copied to the output unchanged.
  • panic is a built-in function that stops the ordinary flow of control and begins panicking. Panics can be initiated by invoking panic directly. They can also be caused by runtime errors, such as out-of-bounds array accesses.
  • We have used the predefined variable os.Stdout which refers to the standard output to print out the merged data – os.Stdout implements io.Writer.
  • Execute applies a parsed template to the specified data object, and writes the output to os.Stdout.
  • The error type is an interface type representing an error condition, with the nil value representing no error. Go code uses error values to indicate an abnormal state.

Pipelines

A pipeline may be “chained” by separating a sequence of commands with pipeline characters '|'. In a chained pipeline, the result of each command is passed as the last argument of the following command. The output of the final command in the pipeline is the value of the pipeline.

The output of a command will be either one value or two values, the second of which has type error. If that second value is present and evaluates to non-nil, execution terminates and the error is returned to the caller of Execute.

Let us look at an example.

Make a new folder and cd to it as follows:

$ mkdir $GOPATH/src/github.com/SatishTalim/person
$ cd $GOPATH/src/github.com/SatishTalim/person

In this folder write the program person.go as follows:

package main

import (
        "os"
        "text/template"
)

type Person struct {
        Name   string
        Emails []string
}

const tmpl = `The name is {{.Name}}.
{{range .Emails}}
    His email id is {{.}}
{{end}}
`

func main() {
        person := Person{
                Name:   "Satish",
                Emails: []string{"satish@rubylearning.org", "satishtalim@gmail.com"},
        }

        t := template.New("Person template")

        t, err := t.Parse(tmpl)

        if err != nil {
                panic(err)
        }

        err = t.Execute(os.Stdout, person)

        if err != nil {
                panic(err)
        }
}

You can now run the program by typing:

$ go run person.go

The output is:

The name is Satish.

    His email id is satish@rubylearning.org

    His email id is satishtalim@gmail.com

In the above program we have {{range .Emails}}. With range the current object . is set to the successive elements of the array or slice Emails.

Variables

The template package allows you to define and use variables. In the above example, how would we print each person’s email address prefixed by their name? Let’s modify the above program.

In the code snippet:

{{range .Emails}}
    {{.}}
{{end}}

We cannot access the Name field as . is now traversing the array elements and the Name is outside of this scope. The solution is to save the value of the Name field in a variable that can be accessed anywhere in its scope. Variables in templates are prefixed by $. So we write:

{{$name := .Name}}
{{range .Emails}}
    Name is {{$name}}, email is {{.}}
{{end}}

The modified program, named new_person.go is:

package main

import (
        "os"
        "text/template"
)

type Person struct {
        Name   string
        Emails []string
}

const tmpl = `{{$name := .Name}}
{{range .Emails}}
    Name is {{$name}}, email is {{.}}
{{end}}
`

func main() {
        person := Person{
                Name:   "Satish",
                Emails: []string{"satish@rubylearning.org", "satishtalim@gmail.com"},
        }

        t := template.New("Person template")

        t, err := t.Parse(tmpl)

        if err != nil {
                panic(err)
        }

        err = t.Execute(os.Stdout, person)

        if err != nil {
                panic(err)
        }
}

You can now run the program by typing:

$ go run new_person.go

The output is:

    Name is Satish, email is satish@rubylearning.org

    Name is Satish, email is satishtalim@gmail.com

The Go template package is useful for certain kinds of text transformations involving inserting values of objects. It does not have the power of, say, regular expressions, but is faster and in many cases will be easier to use than regular expressions.

html/template

Usage: import "html/template"

Package template html/template implements data-driven templates for generating HTML output safe against code injection. It provides the same interface as package text/template and should be used instead of text/template whenever the output is HTML.

In the final part you will learn how to handle forms, build and deploy our Go web app GoView.

I hope you’ve enjoyed this post, please leave any feedback in the comments section.

You can find the other parts of the series at the following links:

5 thoughts on “Learn to build and deploy simple Go Web Apps, Part Three

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.