Skip to content

Wiki: Using Nested Templates #29

@suntong

Description

@suntong

Using Nested Templates

To include other Go templates from within templates, there exist a template function to be used inside template tags, which takes a “template name” and optionally a data cursor. For example the {{ template "header" }} and {{ template "footer" }} in the following example:

{{ template "header" }}
Dear {{ .Name }},
Thank you for your order! Your order number is {{ .OrderNumber }} and it
has been shipped on {{ .ShipDate }}.
{{ template "footer" }}

The driving data format is simple, and can be easily guessed from the template file:

Name: Sam
OrderNumber: 123456
Price: 1234.56
Shipped: true
ShipDate: "Wednesday, January 06, 2021"

To put them together, use the following command (which is a short form for the equally working "easygen nested_header.tmpl,nested_footer.tmpl,nested_thanks.tmpl nested_data.yaml" command):

easygen nested_header,nested_footer,nested_thanks nested_data

It'll give what is expected:

WidgetCo, Ltd.
463 Shoe Factory Rd.
Hamford, VT 20202
Dear Sam,
Thank you for your order! Your order number is 123456 and it
has been shipped on Wednesday, January 06, 2021.
2021-09-25
Thank you for your business,
WidgetCo Order Fulfillment Department
Ph: 818-555-0123 Email: [email protected]

Check out the nested_header.tmpl and nested_footer.tmpl files for more details. And here they are FYC:

{{define "header"}}
WidgetCo, Ltd.
463 Shoe Factory Rd.
Hamford, VT 20202
{{end}}

{{define "footer"}}
{{date "I"}}
Thank you for your business,
WidgetCo Order Fulfillment Department
Ph: 818-555-0123 Email: [email protected]
{{end}}

Credit: the template and data are based from this blog post.

Passing Variables between Templates

The template action used to include nested templates also allows a second parameter to pass data to the nested template. Like the following example from here (Noticed that it was gone as of 2021-09-20, so putting my personal cache here):

// Define a nested template called header
{{define "header"}}
	<h1>{{.}}</h1>
{{end}}

// Call template and pass a name parameter
{{range .Items}}
  <div class="item">
    {{template "header" .Name}}
    <span class="price">${{.Price}}</span>
  </div>
{{end}}

Example in easygen test case ( sgdisk-emb.tmpl & sgdisk-inc.tmpl (Note the usage of {{ template "sgdisk_cmd" .}} in it) ):

easygen/test/sgdisk.yaml

Lines 14 to 25 in bd78192

Partitions:
- Name: bios_boot
Type: ef02
Size: +200M
- Name: linux_boot
Type: 8300
Size: +20G
- Name: windows
Type: "0700"
Size: +30G

sgdisk -Z {{.Disk}}
{{range .Partitions}}
{{ template "sgdisk_cmd" .}}{{end}}

{{define "sgdisk_cmd"}} sgdisk -n 0:0:{{.Size}} -t 0:{{.Type}} -c 0:"{{.Name}}"{{end}}

$ easygen sgdisk-emb,sgdisk-inc sgdisk


 sgdisk -Z /dev/sdb

 sgdisk -n 0:0:+200M -t 0:ef02 -c 0:"bios_boot"
 sgdisk -n 0:0:+20G -t 0:8300 -c 0:"linux_boot"
 sgdisk -n 0:0:+30G -t 0:0700 -c 0:"windows"
 sgdisk -n 0:0:+10G -t 0:8200 -c 0:"linux_swap"
 sgdisk -n 0:0:+12G -t 0:8300 -c 0:"os1"
 sgdisk -n 0:0:+12G -t 0:8300 -c 0:"os2"
 sgdisk -n 0:0:+12G -t 0:8300 -c 0:"os3"
 sgdisk -n 0:0:0 -t 0:8300 -c 0:"data"

Passing more arguments to Nested Templates

What if we need to pass more than one arguments to to the nested template?

Using argsa

Based on

{{define "t1"}}
{{- index . 0}} {{index . 1}} {{index . 2}} {{(index . 3).Name}} {{(index . 3).OrderNumber}}
{{end -}}
Got: {{template "t1" argsa 543 false 0.1234 .}}

Using the above nested_data.yaml file, with

easygen nested_demo_argsa.tmpl nested_data.yaml

will get:

Got: 543 false 0.1234 Sam 123456

Using argsm

Based on

{{template "image_row" argsm "a" 1 "b" "somestring" "c" .}}
{{define "image_row"}}a={{$.a}}, b={{$.b}}, c.Shipped={{$.c.Shipped}}, c.Price={{$.c.Price}}{{end}}

easygen nested_demo_argsm.tmpl nested_data.yaml

will get:

a=1, b=somestring, c.Shipped=true, c.Price=1234.56

Using loop inside a Go template

What if we need to loop over the nested template, and pass the current loop number to the nested template, apart from the rest of the existing arguments?

Based on

{{- define "T1"}}Apple{{.i}} at {{.c.Price}} - {{.c.Shipped}}{{end}} {{- define "T2"}}Ape{{end}}
{{- range $val := iterate "5" }}
{{- template "T2"}} ate {{template "T1" argsm "c" $ "i" (printf "%06d" $val)}}{{ $val }}
{{end }}
{{- range $val := iterate "2" (ENV "__START") }}
{{- template "T2"}} ate {{template "T1" argsm "c" $ "i" (printf "%06d" $val)}}{{ $val }}
{{end }}
{{- range $val := iterate "1003" "1001" }}
{{- template "T2"}} ate {{template "T1" argsm "c" $ "i" (printf "%06d" $val)}}{{ $val }}
{{end }}

easygen nested_demo_argsm_iterate.tmpl nested_data.yaml

will get:

Ape ate Apple000000 at 1234.56 - true0
Ape ate Apple000001 at 1234.56 - true1
Ape ate Apple000002 at 1234.56 - true2
Ape ate Apple000003 at 1234.56 - true3
Ape ate Apple000004 at 1234.56 - true4
Ape ate Apple000005 at 1234.56 - true5
Ape ate Apple000000 at 1234.56 - true0
Ape ate Apple000001 at 1234.56 - true1
Ape ate Apple000002 at 1234.56 - true2
Ape ate Apple001001 at 1234.56 - true1001
Ape ate Apple001002 at 1234.56 - true1002
Ape ate Apple001003 at 1234.56 - true1003

I.e., the undefined (or invalid) environment variable __START will be interpreted as starting at zero (0).
When it is defined:

$ __START=1 easygen nested_demo_argsm_iterate.tmpl nested_data.yaml | diff -wU1 nested_demo_argsm_iterate.ref -
--- nested_demo_argsm_iterate.ref       2021-09-26 09:29:00.172699800 -0400
+++ -   2021-09-26 09:35:48.721988500 -0400
@@ -6,3 +6,2 @@
 Ape ate Apple000005 at 1234.56 - true5
-Ape ate Apple000000 at 1234.56 - true0
 Ape ate Apple000001 at 1234.56 - true1

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions