Skip to content

Tip: Go template usage tips

suntong edited this page Oct 4, 2023 · 4 revisions

How do I check if an element exist?

https://stackoverflow.com/questions/34917099/go-template-comparison-operators-on-missing-map-key

it gives an error if the key doesn't exist

Use the index function:

{{if eq (index .MyMap "KeyThatDoesntExist") "mystring"}}
  {{.}}
{{end}}

playground example

The index function returns the zero value for the map value type when the key is not in the map. The zero value for the map in the question is the empty string.

How do I check the length of an array in Golang templates?

https://dev-qa.com/1835828/how-do-i-check-the-length-of-an-array-in-golang-html-templates

How do I check the length of an array in Golang html templates?

That's the way to do it.

{{ $length := len .YourArray }}  
{{ if eq $length 0 }}  
 array has zero length  
{{ end }}

Simply display the size as follows {{ len .YourArray }}

See

https://github.com/go-easygen/easygen/wiki/Ref:-Golang-Templates-Cheatsheet#template-comparison-functions

See also, https://medium.com/learning-the-go-programming-language/comparing-values-in-go-8f7b002e767a

How to concatenate strings in golang templates?

https://github.com/hashicorp/consul-template/issues/267

You can use Go's template print and printf functions: http://golang.org/pkg/text/template/.

{{$first := "data"}}{{$second := "base"}}
{{range service (print $first $second) "any"}}
upstream {{.ID}} { {{.Address}}:{{.Port}}; } {{end}}

if you need to concatenate an int with a string.

{{ range $hkey, $hval := scratch.Get "map" }}
     {{ $combine := (printf "%v@%v" $hkey $hval) }}
    {{ $combine }}
    {{end}}

https://stackoverflow.com/questions/54156119/range-over-string-slice-in-golang-template

I have a struct that contains a slice of type string like the following.

 type Data struct {
      DataFields []string
 }

Within my html template file I would like to range over the string slice. However, the individual fields are just strings without any struct name. How can I loop over a slice that contains a simple type such as string, int, etc?

Use . to refer to a simple values like a string, int, etc.

 {{range .DataFields}}{{.}}{{end}}

Run it on the Playground.

You can also assign to a template variable as in {{range $v := .DataFields}}{{$v}}{{end}}, but that's extra work. Embrace the ..

Or assign it to a variable, similar to a normal Go range clause:

 {{range $element := .DataFields}} {{$element}} {{end}}

Run it on the Playground

From the docs for text/template (serves as interface docs for html/template):

{{range pipeline}} T1 {{end}}
The value of the pipeline must be an array, slice, map, or channel.
If the value of the pipeline has length zero, nothing is output;
otherwise, <b>dot is set to the successive elements of the array,
slice, or map</b> and T1 is executed. If the value is a map and the
keys are of basic type with a defined order ("comparable"), the
elements will be visited in sorted key order.</pre>
...

A pipeline inside an action may initialize a variable to capture the result. The initialization has syntax

$variable := pipeline

...

If a "range" action initializes a variable, the variable is set to the successive elements of the iteration. Also, a "range" may declare two variables, separated by a comma:

range $index, $element := pipeline

in which case $index and $element are set to the successive values of the array/slice index or map key and element, respectively. Note that if there is only one variable, it is assigned the element; this is opposite to the convention in Go range clauses.

(bold portions emphasized by me)

So one can lookup a constant key on variable map $x with $x.key1, but is it possible to do amap.$key?

A simpler way would be to do: {{.Amap.key1}}. However, this will only work if the key is alphanumeric. If not, you'll need to access it using index.

{{index .Amap "key1"}}

From the [documentation][https://golang.org/pkg/text/template/#hdr-Arguments]:

- The name of a key of the data, which must be a map, preceded
  by a period, such as
	.Key
  The result is the map element value indexed by the key.
  Key invocations may be chained and combined with fields to any
  depth:
    .Field1.Key1.Field2.Key2
  Although the key must be an alphanumeric identifier, unlike with
  field names they do not need to start with an upper case letter.
  Keys can also be evaluated on variables, including chaining:
    $x.key1.key2

How do I access an array item (e.g. a2) in templates?

You need to use the index template function.

{{index .a 2}}

How do I directly access substrings (e.g. s[:2]) in Go templates?

You may use the slice template function on strings too (it was added in Go 1.13):

Template functions:

slice
  slice returns the result of slicing its first argument by the
  remaining arguments. Thus "slice x 1 2" is, in Go syntax, x[1:2],
  while "slice x" is x[:], "slice x 1" is x[1:], and "slice x 1 2 3"
  is x[1:2:3]. The first argument must be a string, slice, or array.

For example:

t := template.Must(template.New("").Parse(`{{ slice . 0 2 }}`))
if err := t.Execute(os.Stdout, "abcdef"); err != nil {
	panic(err)
}

This will output (try it on the Go Playground):

ab

Don't forget that Go strings store the UTF-8 encoded byte sequence, and indexing and slicing strings uses the byte index (not the rune index). This matters when the string contains multi-byte runes, like in this example:

t := template.Must(template.New("").Parse(`{{ slice . 0 3 }}`))
if err := t.Execute(os.Stdout, "世界"); err != nil {
	panic(err)
}

This will output a single rune (try it on the Go Playground):

https://go.dev/play/p/olgfaazf5tb

package main

import (
    "os"
    "text/template"
)

func main() {
    t := template.Must(template.New("").Parse(`{{ slice . 3 9 }}`))
    if err := t.Execute(os.Stdout, "abc世界def"); err != nil {
	    panic(err)
    }
}