Welcome to part 16 of the Go Language tutorial series, in this tutorial we will be covering HTML templates with Go. While we can do everything in your .go
file, this can become...challenging, especially for larger projects with more complex HTML, styling, as well as the incorporation of JavaScript. With Go, we can use templating and some basic logic from within our HTML files, which will be parsed by Go's html/template
package
Besides this tutorial, you should check out the html/template Golang documentation, as we'll just cover some basics and then use it for our needs.
To begin, we'll start with a basic "hello world" example of a golang webapp:
package main import ( "fmt" "net/http" ) func indexHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "<h1>Whoa, Go is neat!</h1>") } func main() { http.HandleFunc("/", indexHandler) http.ListenAndServe(":8000", nil) }
So here, we've not used any templating, and we're just returning a simple string of HTML. Now, let's add a new page that utilizes an HTML template.
We'll put this new page under /agg/
, since our overarching project is a "news aggregator." So we'll change our main
function to:
func main() { http.HandleFunc("/", indexHandler) http.HandleFunc("/agg/", newsAggHandler) http.ListenAndServe(":8000", nil) }
Next, we can only pass one object to our template, so we're going to use a struct to be able to pass multiple values. We'll call this struct NewsAggPage
, and, for now, we'll just make it fairly arbitrary:
type NewsAggPage struct { Title string News string }
Next, before we build our handler, let's not forget to import the html/template
import ( "fmt" "net/http" "html/template" )
Now, let's build our handler, which we'll call newsAggHandler
.
func newsAggHandler(w http.ResponseWriter, r *http.Request) { }
First, we need our page data, which we decided to put into a struct so we could have multiple values. For now, we'll just populate it with something arbitrary:
func newsAggHandler(w http.ResponseWriter, r *http.Request) { p := NewsAggPage{Title: "Amazing News Aggregator", News: "some news"} }
Once we've got the page variables that we want, we also need to bring in the template file:
func newsAggHandler(w http.ResponseWriter, r *http.Request) { p := NewsAggPage{Title: "Amazing News Aggregator", News: "some news"} t, _ := template.ParseFiles("basictemplating.html") }
Finally, we can execute the template, with our page variables:
func newsAggHandler(w http.ResponseWriter, r *http.Request) { p := NewsAggPage{Title: "Amazing News Aggregator", News: "some news"} t, _ := template.ParseFiles("basictemplating.html") t.Execute(w, p) }
Our full Go script now:
//start with web7 / edit7, then we bring in combined2.go. package main import ( "fmt" "net/http" "html/template" ) type NewsAggPage struct { Title string News string } func indexHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "<h1>Whoa, Go is neat!</h1>") } func newsAggHandler(w http.ResponseWriter, r *http.Request) { p := NewsAggPage{Title: "Amazing News Aggregator", News: "some news"} t, _ := template.ParseFiles("basictemplating.html") t.Execute(w, p) } func main() { http.HandleFunc("/", indexHandler) http.HandleFunc("/agg/", newsAggHandler) http.ListenAndServe(":8000", nil) }
Now, we just need to build our basictemplating.html
template file. For this simple case:
<h1>{{.Title}}</h1> <p>{{.News}}</p>
So you use the double curly braces to notify the template parser that you're intending to pass some sort of value here, and then you can use the dot and the object's name.
We're all set. go run
your main webapp file, and visit /agg/
to see your variables live!
Now, of course, what we've done here is something we could have easily done without templates. Let's bring our new skill back to our news aggregator app and see if we can display our findings.