How to make headers, footers, etc. common in Go's html/template

I’ve been teaching the Go language to newcomers lately, and I thought “this is definitely difficult to understand”, so I’d like to pick up one of the points to explain. It’s “I tried to divide a template in Go, but I couldn’t pass the value properly and gave up. How did you do this? The question was, “How can I use this template engine?

Go’s standard library html/template (not text/template) is an amazingly simple, yet very powerful template engine. I’m currently developing and running a web application with 43 screens using this html/template, some of them lightly over 1000 lines of HTML, and every page renders fast. Also, if you master the componentization described in this article, you can achieve something similar to the componentization we do in front-end frameworks to a certain extent.

Before I show you the answers to the questions at the beginning of this article, let’s review the basic usage of the front-end framework.

Review: html/template basics

First, we need to prepare the following files.

``. . ├──── server.go └ └ ── template. └──────── index.html


Let's create a `index.html`.

``index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{.Title}}</title>
</head>
<body>.
<h1>{{.Title}}</h1>
<p>{{.Message}}</p>
<p>Current Time: {{.Time.Format "2006/1/2 15:04:05"}}</p>
</body>.
</html>

Let’s write the server.go. The template index.html above refers to three values, Title, Message and Time, and you can run the template by supplying the values. Supply the values and run the template.

You can run the template by supplying the values for the `go package main

import ( “html/template”. “log”. “net/http”. “time” )

func main() { port := “8080” http.HandleFunc("/”, handleIndex) log.Printf(“Server listening on port %s”, port) Print(http.ListenAndServe(": “+port, nil)) }

func handleIndex(w http.ResponseWriter, r *http.Request) { t, err := template.ParseFiles(“template/index.html”) if err ! = nil { log.Fatalf(“template error: %v”, err) } if err := t.Execute(w, struct { Title string Message string Time time.Time }{ Title: “Test Page”, Message: “Hello!” , Time: time.Now(), }); err ! = nil { log.Printf(“failed to execute template: %v”, err) } }


The `handleIndex` function reads a template from a file, parses it, fills it with values, and then executes it.
Here, an anonymous struct is created on the fly, but you can also pass it as a map. It is possible to pass it in as a map, but the type of value must be the same (or `interface{}`).

The `main` function maps the path to the function and starts the server. Let's run it with the command `go run server.go`.

It's a perfect fit! Now, if you read (and write) the code, you'll find it inefficient to load the template file every time a request comes in (which is nice when you're debugging a template). We'll try to improve it by loading it at startup. We'll try to improve it by loading the ``go // import has been omitted. var templates = make(map[string]*template.Template) func main() { port := "8080" templates["index"] = loadTemplate("index") http.HandleFunc("/", handleIndex) log.Printf("Server listening on port %s", port) Print(http.ListenAndServe(": "+port, nil)) } func handleIndex(w http.ResponseWriter, r *http.Request) { if err := templates["index"].Execute(w, struct { Title string Message string Time time.Time }{ Title: "Test Page", Message: "Hello!" , Time: time.Now(), }); err ! = nil { log.Printf("failed to execute template: %v", err) } } func loadTemplate(name string) *template. t, err := template.ParseFiles("template/" + name + ".html") if err ! = nil { log.Fatalf("template error: %v", err) } return t }

It’s starting to look like it!

Then it’s time to answer the question.

First of all, let’s create a file, _header.html and _footer.html. Let’s name them _header.html and _footer.html. This is just a practice, but we want to make sure that the names indicate that they are components. I decided to keep it simple and add the ansko. Maybe some of you might like to keep the directories separate. Of course, that’s fine, too.

├──── server.go
└ └ ── template
    ├──── _footer.html
    ├──── _header.html
    └──────── index.html

To define a part, set {{define "<NAME>"}}}. You also need `{{{end}}} on the butt.

Let’s start with _header.html. This time, I’ve tried to make it look like this.

``_header.html {{define “header”}}}

The next one is _footer.html. We’ve tried to make this one static, with no variable parts.

The ``html {{define “footer”}}}

What will happen to the original index.html? Here’s the thing. This is what happens!

``html {{template “header” .}}

Did you notice a slight difference between the header and footer calls? The header we just wrote makes use of a value, while the footer doesn’t make use of a value. But footer doesn’t make use of a value. In the case of a dynamic template with values, you need to pass a value to the template. If it is a static template without values, you don’t need to pass anything to the template. This little difference is probably one of the first things that first learners get into.

Now, once you’ve prepared the template, you need to make sure that the Go code reads it too. There’s only one point to fix, though: the loadTemplate' we just created. It's the loadTemplatefunction that we created earlier, and it's the one we're going to fix. TheloadTemplate` function we just created is the one we’re going to fix.

The ``go func loadTemplate(name string) *template. t, err := template.ParseFiles( “template/“+name+”.html”, “template/_header.html”, “template/_footer.html”, ) if err ! = nil { log.Fatalf(“template error: %v”, err) } return t }


The function `ParseFiles()` has, as the name of the function suggests, a variable-length argument that takes multiple paths.
It passes the top-level template as the first argument, and then enumerates the **parts** behind it.

In fact, there's a hookup point here as well. As I emphasized in bold, `ParseFiles()` returns the template identifier of the template as the first argument.
So don't put the parts at the beginning. Parts should be written at the end. Components can be placed in any order (even if the footer comes before the header).

If you go past the two add-on points, you have completed the componentization. Let's run the program again.

The part called "test page" that I moved to the header and the description in the footer appears successfully! OK, I totally get it! Is that the end of this article's role? Do you want to close this browser tab? Well, that's okay. But I have one more piece of advice for you! ### Separating the data used by the parts. Once you can handle the parts, you can take it one step further. Imagine a web application with many pages. What would happen if you wanted a new value for the header, and you didn't fill the struct of all the existing pages with the new value? Of course it will break. You don't want to maintain such a web application, do you? So we solve this by providing special structs and useful functions for the parts of templates that use values. Let's start with the header template. We've added a line that uses the `UserName` value. We've added a line that uses the value ``html {{define "header"}}} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{{.Title}}</title> </head> <body>. <h1>{{.Title}}</h1> <p>Welcome {{.UserName}}! </p> <p>Welcome! {{end}}

The corresponding struct and useful functions are written in Go.

The ```` type header struct { Title string UserName string }

func newHeader(title string) header { return header{Title: title, UserName: “guest”} }


We use this structure from the handler and pass it to the template.

``go
func handleIndex(w http.ResponseWriter, r *http.Request) {
	if err := templates["index"].Execute(w, struct {
		Header header
		Message string
		Time time.Time
	}{
		Header: newHeader("test page"),
		Message: "Hello!" ,
		Time: time.Now(),
	}); err ! = nil {
		log.Printf("failed to execute template: %v", err)
	}
}

The index.html passes the Header from the handler to the header template.

The `index.html {{template “header” .Header}}

{{template "header" .}} {{{template “header” .Header}}}is now{{template “header” .Header}}. Now, the scope of the part is in the header.

And when you run it…

The UserName is displayed!

Note that it is also possible to apply such a change without modifying the index template ({{template "header" .}} ) can also be applied without modifying the template of index(as{{template “header” . UserName}} {{{{.Header.UserName}}}`` instead of {{.

Conclusion

With a combination of basic Go techniques and a few template tricks, it’s possible to develop even moderately large web applications without much trouble.

Also, there is a feature called FuncMap, which I won’t cover in this article. With this feature, you can call any Go function from the template, which gives you more expressive power. Please learn it along with the composite template.

All the html/template specifications are in Godoc, so if you’re having trouble, just look here for the answers.