When a web service responds with data or html pages, there is usually a lot of content that is standard. Within that there needs to be modifications done based on the user and what has been requested. 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.
In Go, we use the
Typical usage of templates is within html code that is generated from the server side. We would open a template file which has already been defined, then merge that with some data we have using
We should be seeing other examples of actual html code which utilizes this functionality. But for the purposes of learning, to write out all that html is unnecessary clutter. So for learning purposes, we shall use simpler code where I can illustrate the template concepts more clearly.
* Instead of
* Instead of writing it as a web service, we shall write code we can execute from the command line
* We shall use the predefined variable
For the sake of completeness, let’s do an example where there is an error due to a missing field. In the below code, we have a field
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.Typical usage of templates is within html code that is generated from the server side. We would open a template file which has already been defined, then merge that with some data we have using
template.Execute
which writes out the merged data into the io.Writer
which is the first parameter. In the case of web functions, the io.Writer
instance is passed into the handler as http.ResponseWriter
.Partial code
func handler(w http.ResponseWriter, r *http.Request) { t := template.New("some template") //create a new template t, _ = t.ParseFiles("tmpl/welcome.html", nil) //open and parse a template text file user := GetCurrentlyLoggedInUser() //a method we have separately defined to get the value for a type t.Execute(w, user) //substitute fields in the template 't', with values from 'user' and write it out to 'w' which implements io.Writer }
We should be seeing other examples of actual html code which utilizes this functionality. But for the purposes of learning, to write out all that html is unnecessary clutter. So for learning purposes, we shall use simpler code where I can illustrate the template concepts more clearly.
* Instead of
template.ParseFiles
to which we have to pass one or more file paths, I shall use template.Parse
for which I can give the text string directly in the code where it would be easier for you to see.* Instead of writing it as a web service, we shall write code we can execute from the command line
* We shall use the predefined variable
os.Stdout
which refers to the standard output to print out the merged data - os.Stdout
implements io.Writer
Field substitution - {{.FieldName}}
To include the content of a field within a template, enclose it within curly braces and add a dot at the beginning. E.g. ifName
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. Full program
package main import ( "os" "text/template" ) type Person struct { Name string //exported field since it begins with a capital letter } func main() { t := template.New(“hello template”) //create a new template with some name t, _ = t.Parse("hello {{.Name}}!") //parse some content and generate a template, which is an internal representation p := Person{Name:"Mary"} //define an instance with required field t.Execute(os.Stdout, p) //merge template ‘t’ with content of ‘p’ }
hello Mary!
For the sake of completeness, let’s do an example where there is an error due to a missing field. In the below code, we have a field
nonExportedAgeField
, which, since it starts with a small letter, is not exported. Therefore when merging there is an error. You can check for the error on the return value of the Execute
function. Full program
package main import ( "os" "text/template" "fmt" ) type Person struct { Name string nonExportedAgeField string //because it doesn't start with a capital letter } func main() { p:= Person{Name: "Mary", nonExportedAgeField: "31"} t := template.New("nonexported template demo") t, _ = t.Parse("hello {{.Name}}! Age is {{.nonExportedAgeField}}.") err := t.Execute(os.Stdout, p) if err != nil { fmt.Println("There was an error:", err.String()) } }
hello Mary! Age is There was an error: template: nonexported template demo:1: can't evaluate field nonExportedAgeField in type main.Person
template Must function - to check validity of template text
The staticMust
function checks for the validity of the template content, i.e. things like whether the braces are matches, whether comments are closed, and whether variables are properly formed, etc. In the example below, we have two valid template texts and they parse without causing a panic. The third one, however, has an unmatched brace and will panic.Full program
package main import ( "text/template" "fmt" ) func main() { tOk := template.New("first") template.Must(tOk.Parse(" some static text /* and a comment */")) //a valid template, so no panic with Must fmt.Println("The first one parsed OK.") template.Must(template.New("second").Parse("some static text {{ .Name }}")) fmt.Println("The second one parsed OK.") fmt.Println("The next one ought to fail.") tErr := template.New("check parse error with Must") template.Must(tErr.Parse(" some static text {{ .Name }")) // due to unmatched brace, there should be a panic here }
The first one parsed OK.
The second one parsed OK.
The next one ought to fail.
panic: template: check parse error with Must:1: unexpected "}" in command
runtime.panic+0xac ...
The second one parsed OK.
The next one ought to fail.
panic: template: check parse error with Must:1: unexpected "}" in command
runtime.panic+0xac ...
Sorry but when I need to have something in curly braces but don't want it to be parsed by template engine, what should I do then ?
ReplyDeleteFor example javascript in html template.
@Coder_
ReplyDeleteI think you can use double curly braces:
{{Name}}
Or you could just load the JavaScript from an external file so that it isn't parsed at all.
I believe the template package just changed.. anyways this template code looks foreign compared to the work I'm doing.
ReplyDeleteThank you Seda, I have updated the basics of the template package usage. Will add all the other changes also as soon as possible.
ReplyDeleteHello,
ReplyDeleteIn the example with nonExportedAgeField the Execute method is missing. The code should be like:
t := template.New("nonexported")
t, _ = t.Parse("hello {{.Name}}!")
p := Person{Name:"Mary", nonExportedAgeField: "31"}
err := t.Execute(os.Stdout, p)
if err != nil {
fmt.Println("There was an error:", err.String())
When I compile/run this program with r60 I don't get any errors: non exported fields are simply discarded.
greetings,
Ivo
To get the error you must do something like:
ReplyDeletet, _ = t.Parse("your age is {{.nonExportedAgeField}}!")
Thank you Ivo. I think I goofed up during copy-paste from my code file. I have updated the correct one now with the correct output.
ReplyDeleteI have no words for this great post such a awe-some information i got gathered. Thanks to Author.
ReplyDeleteTHANK YOU!!! I was wondering why my {{.Model.firstName}} wasn't working - changed it to {{.Model.FirstName}} (note the capital F) in both the .go file and in the .html template, and it's working perfectly now! Amazing - lower case results in a non exported field. Thanks!
ReplyDeleteHi, I'm having trouble understanding the difference between and uses of the different equals signs (":=","==" and "="), I can change them but In this example i have no idea why theres an "=" sign before you parse t, whats the meaning behind it?
ReplyDelete:= means "declare t as a variable and assign", = means "assign to an already existing variable", == means to test for equality
ReplyDeleteI very much enjoyed reading your blog post. It has helped alot.
ReplyDeleteIf possible, as more knowledge comes your way, I hope you could update to share more information with us. It is very helpful.
I also know of more informational sources that could benefit your readers, please find links below
funeral program template
obituary template
memorial service program
Interesting tutorial and the blog was awesome.. thanks for share this blog....
ReplyDeleteTHANK YOU!!! I was wondering why my {{.Model.firstName}} wasn't working
ReplyDeleteGreat examples.
ReplyDeleteUnfortunately your code is difficult to read because the lines are too long for Blogger. Maybe you could use Google Drive or Google Sites and create links to the files/pages?
Nevermind, I can use copy-paste to my text editor. ;)
I believe there are many other people who are interested in them just like me! Thank you for sharing them with everyone!
ReplyDeleteYour article is awesome! How long does it take to complete this article? I have read through other blogs, but they are cumbersome and confusing. I hope you continue to have such quality articles to share with everyone! I believe there will be many people who share my views when they read this article from you!
ReplyDeleteThanks for your post! Through your pen I found the problem up interesting! I believe there are many other people who are interested in them just like me! Thanks your shared!... I hope you will continue to have similar posts to share with everyone! I believe a lot of people will be surprised to read this article!
ReplyDelete