Nesting Templates and Layouts with Go Web Programming

By

Style A ReadMe

Sau Sheong Chang

 

What exactly are layouts?

Layouts are fixed patterns in web page design that can be re-used for multiple pages. Web applications often use layouts, as pages in a web application need to look the same for a consistent user interface. For example, many web application designs have a header menu as well as a footer that provides additional information such as status or copyright or contact details. Other layouts include a left navigation bar or multi-level navigation menus. Its easy to see that layouts can be implemented with nested templates.

 

Templates can be nested using the include action.  While this seems to be all that we need, if you start writing a more complicated web application you’ll realize that you might end up with a lot of hardcoding in your handler and a lot of template files.

 

Why is this so?

Remember that the syntax of the include action is this:

 

{{ template "name" . }}

 

Where “name” is the name of the template and it is a string constant. This means that if we default in using the name of the file as the template name, it will be impossible to have one or two common layouts, since every page will have its own layout template file, which defeats the purpose of having layouts in the first place. For example, this will not work as a common layout template file.

Listing 1 – An unworkable layout file

<html>

  <head>

    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

    <title>Go Web Programming</title>

  </head>

  <body>

    {{ template "content.html" }}

  </body>

</html>

 

The answer to this dilemma is that the Go template engine doesn’t work this way. While we can have each template file define a single template, with the name of the template being the name of the file name, we can actually explicitly define a template in a template file using the define action. Let’s take a look at our layout.html now.

 

Listing 2 – Defining a template explicitly

{{ define "layout" }}

 

<html>

  <head>

    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

    <title>Go Web Programming</title>

  </head>

  <body>

    {{ template "content" }}

  </body>

</html>

 

{{ end }}

 

Notice that we start the file with {{ define "layout" }} and end the file with {{ end }}.  Anything within these two action tags are considered part of the layout template. This means if we have another define action tag after {{ end }} we can actually define another template! In other words, we can define multiple templates in the same template file.

 

Listing 3 – Defining multiple templates in a single template file

{{ define "layout" }}

 

<html>

  <head>

    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

    <title>Go Web Programming</title>

  </head>

  <body>

    {{ template "content" }}

  </body>

</html>

 

{{ end }}

 

{{ define "content" }}

 

Hello World!

 

{{ end }}

 

Listing 4 – Using explicitly defined templates

 

func process(w http.ResponseWriter, r *http.Request) { 

  t, _ := template.ParseFiles("layout.html")

  t.ExecuteTemplate(w, "layout", "")

}

 

Parsing the template file is still the same, but this time if we want to execute the template, we have to be more explicit and use the ExecuteTemplate method, with the name of the template we want to execute as the second parameter. The layout template nests the content template, so if we execute the layout template, we will see Hello World! shown on the browser. Let’s use curl to get the actual HTML so that we can see it properly.

 

> curl -i http://127.0.0.1:8080/process

HTTP/1.1 200 OK

Date: Sun, 08 Feb 2015 14:09:15 GMT

Content-Length: 187

Content-Type: text/html; charset=utf-8

 

<html>

  <head>

    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

    <title>Go Web Programming</title>

  </head>

  <body>

Hello World!

  </body>

</html>

 

Conversely, we can also define the same template in the multiple template files. To see how this works, let’s remove the definition of the content template in layout.html and place it in red_hello.html.

 

Listing 5 – red_hello.html

 

{{ define "content" }}

 <h1 style="color: red;">Hello World!</h1>

 {{ end }}

 

Now create a blue_hello.html template file.

 

Listing 6 – blue_hello.html

 

{{ define "content" }}

<h1 style="color: blue;">Hello World!</h1>

{{ end }}

 

Notice that we’ve just defined the content template in two places. How can we use these two templates? Let’s take a look at our modified handler.

 

Listing 7 – Handler using the same template in different template files

 

func process(w http.ResponseWriter, r *http.Request) { 

  rand.Seed(time.Now().Unix())

  var t *template.Template

  if rand.Intn(10) > 5 {

    t, _ = template.ParseFiles("layout.html", "red_hello.html")

  } else {

    t, _ = template.ParseFiles("layout.html", "blue_hello.html")

  }

  t.ExecuteTemplate(w, "layout", "")

}

 

Note that we’re actually parsing different template files (either red_hello.html or blue_hello.html) according to the random number we create. We use the same layout template as before, which includes a content template. Remember that the content template is defined in two different files. Which actual template we use depends now on which template file we parse since both of these template files define the same template. In other words, we can switch different content by parsing different template files, while maintaining the same template to be nested in the layout.

 

If we now re-compile our server, start it and access it through the browser, we’ll randomly see either a blue or red Hello World! showing up on the browser.

You can learn more in my book Go Web Programming, available from Manning Publications, Inc.

This article is exceprted from Go Web Programming by Manning Publishing

 

go

Go Web Programming

By Sau Sheong Chang

ISBN: 9781617292569

 

 



Make a Comment

Loading Comments...

  • Web Development Newsletter Signup

    Invalid email
    You have successfuly registered to our newsletter.
  •  
  •  
  •  
Thanks for your registration, follow us on our social networks to keep up-to-date