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 astruct
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
implementsio.Writer
. Execute
applies a parsed template to the specified data object, and writes the output toos.Stdout
.- The
error
type is aninterface
type representing an error condition, with thenil
value representing no error. Go code useserror
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”