DEV Community

Deepak Ahuja 👨‍💻
Deepak Ahuja 👨‍💻

Posted on • Updated on

Unlock SSR Superpowers (Learn and Use Templates In Go)

Photo by John Doyle Unsplash

While developing several full stack apps over years, I realised some apps don’t
need a full fledged frontend application running. Most of these apps serve
static content with dynamic values fitted here and there. For example, consider
your social media feed, each post of a certain type looks same but it gets
populated with data specific to that user. They use some sort of templating to
achieve it.

What are templates?

Templates are essentially text files that are used to create dynamic content.
For instance the following javascript function takes “name” as argument and
produce different strings.

We can also use same principal to generate web pages in Go. Web templates allow
us to serve personalised results to users. Generating web templates using string
concatenation is a tedious tasks. It can also lead to injection attacks.

Templates in go

There two packages in go to work with templates:

  1. text/templates (Used for generating textual output)
  2. html/templates (Used for generating HTML output safe against code injection)

Both of them basically have same interface with subtle web specific differences
in latter like encoding script tags to prevent them from running, parsing maps
as json in view.

Our First Template

Web template in go
  1. The extension of template file could be anything. We are using .gohtml so that it is supported development tools across IDEs.
  2. “Actions” — data evaluations or control structures — is delimited by{{and}}. The data evaluated inside it is called Pipeline. Anything outside them is send to output unchanged.
Code to execute the template with data: Use go run main.go to see output

You can parse multiple files on line:10 as comma separated strings (path to
file
) or parse all files inside a directory. For example:

tpl, err := template.ParseFiles(“index1.gohtml”, “index2.gohtml”)
tpl, err := template.ParseGlob("views/templates/*")
  1. It returns a template container (here called tpl) and error. We can use our data to execute tpl *by calling method Execute. *In case of multiple templates, The name of template will be passed as 2nd argument while data being 3rd.
  2. We define our data structure (here type struct). It could be anything in go slice, map, struct, slice-of-structs, structs-of-slice of structs. We will see each of them shortly.
  3. The data is fetched using *dot (aka cursor). *We use it to access variables of data inside templates. Remember the identifiers in provided data have to start with Capital Case. We can use them to initialise a variable inside Actions like$myCoupon := .Coupon.
  4. We can output the execution result to web page or standard output because template Execute method takes any value which implements type Writer interface.

It renders as plain text in output console, but If we use it to send as response
to web request it will be rendered as a web page enabling us to use HTML tags.

Standard template output

Web template output

It is preferred to do the parsing job inside init function and execution at
required places.

func init() {
 // Must is a helper that wraps a function returning  
 // (*Template, error) and panics if the error is non-nil.
 tpl = template.Must(template.ParseGlob(“templates/*”))
}

Using different data structures in web templates

Slice (or Array)

Let’s consider a
slice of
strings:

Slice of strings in go

This can be used in template by ranging over the Slice (also array) inside
Actions. If the value of the pipeline has length zero, nothing will be
executed
otherwise, dot(aka cursor) is set to the successive elements of
the Slice (also array) and template is executed.

Template for range over slice

Web template output

Map

Let’s consider a Map data
structure:

Map in go

The value of the pipeline inside Action is a map. If there are no key values
pairs in map, nothing is output, otherwise, dot (aka cursor) is set to the
successive elements of the map and template is executed with $key taking each
key, $val taking the respective value. If keys are of basic type with a
defined order ("comparable"), the elements will be visited in sorted key order.

Web template for map

Web template output

Struct

Let’s consider a struct with collection of
fields as declared
inline:

Struct in go

The name of a field of the data struct, preceded by a period, such as .Name is
the argument in template. The result is the value of the field. Field
invocations may be chained: .Field1.Field2. Fields can also be evaluated on
variables, including chaining: $x.Field1.Field2 . The following template shows
the fields store in variables and then evaluated in Actions.

Web template for struct in go


Web template output

Slice of structs

Let’s consider a slice of structs :

Slice to struct in go

We use range to iterate over slice. The value of the pipeline in Action must be
an array (or slice). If the value of the length of slice is zero, nothing is
output; otherwise, dot (aka cursor) is set to the successive elements of the
array, slice and Template is executed.

Web template for slice of structs

Like this different data structures in go can be combined to make useful
templates.

A note on Conditionals in templates:

It is also possible to write conditionals templates {{if pipeline}} T1 {{else if pipeline}} T0 {{end}} like this. This gives us amazing abilities to generate dynamic content. If value of pipeline is empty (that is, false, 0, any nil pointer or interface value, and any array, slice, map, or string of length zero), T0 is executed else T1 is executed.

Function In templates

To send a function in go template we can use, by default, no functions are
defined in the template but the Funcs method on template can be used to add
function by creating mappings like this:

template.Must(template.New(“”).Funcs(fm).ParseFiles(“index.gohtml”))

Initialising a function for template in Go

To create a mapping from names to functions we need to use type FuncMap. Each
function must have either a single return value, or two return values of which
the second has type error.

type FuncMap map[]interface{}

On line:7, we have defined a mapping of function monthDayYear to *name
fdateMDY. Now this function can be used inside template. Remember dot (aka
*cursor
) holds the data provided to template.

Web template in go using function

These are various ways we can use templates in Go. I will publish how to use
what we learn’t to build our own working web pages using nested templates. You
can drop me hello with questions and feedback on
twitter. Please consider leaving a feedback and share with anyone who may benefit from it. Thanks!

Top comments (0)