Using Templates in Go

Templates in Go are provided through the built-in package “text/template” and “html/template”. In this post, we’ll use the latter to show how to format simple web pages in Golang.

For our program’s web services, we’ll be using the popular “gorilla/mux” package.

Templates in Go rely on data from structs. We’re going to make a simple (very simple!) “Contacts” app, so we’ll create a parent struct (the user who owns the contact list) plus a child struct to hold the individual contacts.

type Entry struct {
	Name   string
	Email  string
	Mobile string
}
type Contacts struct {
	User string
	List []Entry
}

As you may already know, Go doesn’t have classes. The struct, however, gives us some of the same functionality. In this example, we have an Entry struct for the recurring data that will make up our list of contacts, and that struct is used as a type in the Contacts struct. By putting “[]” in front of Entry in Contacts, we make sure we can have more than one contact.

Typically data for an app like this would be stored in a database, but this is a templating tutorial so we’ll skip that part. Instead, let’s just put in a function that will load in some hard-coded data for us:

func getData() Contacts {
	var contacts Contacts

	contacts.User = "Kenn"
	contacts.List = append(contacts.List, NewEntry("Jane Dough", "janedough@example.com", "123-456-7890"))
	contacts.List = append(contacts.List, NewEntry("Jon Dough", "jondough@example.com", "234-567-8901"))
	contacts.List = append(contacts.List, NewEntry("Sour Dough", "sourdough@example.com", "345-678-9012"))

	return contacts
}

What we’ve done here is defined a local “contacts” from our Contacts struct, and then loaded it with a few rows by appending dummy contact info.

Called from the getData function (above), the NewEntry function was added just to make the append statements a little cleaner.

func NewEntry(name string, email string, mobile string) Entry {
	return Entry{
		Name:   name,
		Email:  email,
		Mobile: mobile,
	}
}

We’re going to put the bulk of our functionality in a function called contactsMain, but before we do that, let’s skip ahead to this package’s main function, in which we’ll set up our router and web server:

func main() {
	r := mux.NewRouter()

	r.PathPrefix("/css/").Handler(http.StripPrefix("/css/", http.FileServer(http.Dir("./public/css"))))
	r.PathPrefix("/js/").Handler(http.StripPrefix("/js/", http.FileServer(http.Dir("./public/js"))))

	r.HandleFunc("/", contactsMain)

	srv := &http.Server{
		Handler:      r,
		Addr:         "127.0.0.1:8080",
		WriteTimeout: 15 * time.Second,
		ReadTimeout:  15 * time.Second,
	}

	log.Fatal(srv.ListenAndServe())
}

Notice those two lines for the CSS and JS folders: that’s how we’re serving our static resource folders using the gorilla/mux package. (There’s actually no Javascript in this tutorial but we have the spot if we need it!)

We’ve set up the function “contactsMain” to be the go-to spot for all direct requests to our domain (which will be http://localhost:8080).

We’re going to need folders for our CSS and JS, so go ahead and make those. For this example, I’m using W3.css and I made another CSS for my site’s color scheme:

.primary-color {
    background-color: #304173;
    color: #fffafa;
}
.secondary-color {
    background-color: #29A2EE;
    color: #fffafa;
}
.dark-accent {
    background-color: #605B56;
}
.light-accent {
    background-color: #D3C47F;
}
.off-white {
    background-color: #ECEBE4;
}

Now we’re all set to host our contacts page on http://localhost:8080 with one minor exception: the main part of our program!

Our program is going to have three templates. For this example, I’ve created a subfolder in my go project called “templates” and in that I’m going to have a file for my site’s header (contact-header.tmpl) and another for my site’s footer (contact-footer.tmpl):

{{ define "header" }}
<!DOCTYPE html>
<html>
  <head>
    <title>Go To-Do list</title>
    <link rel="stylesheet" type="text/css" href="/css/w3.css">
    <link rel="stylesheet" type="text/css" href="/css/colors.css">
  </head>
  <body>
  <div class="w3-bar primary-color">
    <a href="/" class="w3-bar-item w3-button w3-mobile">Home</a>
    <a href="#" class="w3-bar-item w3-button w3-mobile">Services</a>
    <a href="#" class="w3-bar-item w3-button w3-mobile">About Us</a>
    <a href="#" class="w3-bar-item w3-button w3-mobile">Contact Us</a>
  </div>
{{ end }}

(I’m using w3.css because I love how well its documented and it makes it easy to make things look nice!)

{{ define "footer" }}
  <br />
  <div class="w3-cell-row primary-color">
    <div class="w3-container w3-cell">
      <p class="w3-small">This is an unassuming and unobtrusive footer.</p>
    </div>
  </div>
  </body>
</html>
{{ end }}

My main body template is called contact-main.tmpl:

{{ define "layout" }}
  {{ template "header" }}
<div class="w3-container">
	<br />
	<div class="w3-card-4">

		<header class="w3-container secondary-color">
			<h1>{{ .User }}'s Contact List</h1>
		</header>

		<div class="w3-container">
    <table class="w3-table w3-striped w3-bordered">
      	<tr>
          <th>Name</th>
          <th>Email</th>
					<th>Mobile</th>
    	</tr>
      	{{ with .List }}
			{{ range . }}
      			<tr>
              		<td>{{ .Name }}</td>
									<td>{{ .Email }}</td>
									<td>{{ .Mobile }}</td>
      			</tr>
			{{ end }} 
      	{{ end }}
    </table>
		<br />
		</div>
	</div>
</div>
  {{ template "footer" }}
{{ end }}

So what’s going on with those three files? The header and footer files start with “{{ define “header” }}” and “{{ define “footer” }}” respectively, and both end with “{{ end }}”. Those are the html/templates packages delimiters; it tells Go that everything in the middle is part of a template identified by the name you provided.

In the main (“layout”) template, we include header and footer with the “{{ template “header” }}” and “{{ template “footer” }}” statements.

Further down, we define our HTML table normally, but for the repeating section, we use looping syntax:

</tr>
{{ with .List }}
    {{ range . }}
	<tr>
	    <td>{{ .Name }}</td>
	    <td>{{ .Email }}</td>
            <td>{{ .Mobile }}</td>
      	</tr>
    {{ end }} 
{{ end }}
</table>

With .List and range, we’re able to iterate through all the contacts with minimal coding; those few lines are enough to get us this:

Example output from our contacts list.

The function that brings all this together is contactsMain:

func contactsMain(w http.ResponseWriter, r *http.Request) {
	contacts := getData()

	paths := []string{
		"templates/contact-main.tmpl",
		"templates/contact-header.tmpl",
		"templates/contact-footer.tmpl",
	}

	contactTmpl, _ := template.ParseFiles(paths...)

	err := contactTmpl.ExecuteTemplate(w, "layout", contacts)
	if err != nil {
		panic(err)
	}
}

As previously mentioned, our getData() function is a bit of Wizard-of-Oz prototyping, just returning hard-coded data. (We could, of course, replace this function with a database read if we were shooting for something more useful.)

The paths variable is a list of our template files, which we parse into contactTmpl before using ExecuteTemplate to get them on the screen.

Hopefully this has provided a useful overview of templates in Go. Feel free to put any questions you might have in the comments below and I’ll do my best to answer.il

The full source for this tutorial can be found on Github.

 

Leave a Comment

Your email address will not be published. Required fields are marked *