Template variable scoping

This is one of those go template variable scoping questions. I’m assuming the same variable scoping apply in shortcodes as partials and templates. This context is a shortcode.

The goal is to have the same shortcode process different data files based on a context provided by input parameters. I don’t see how to pass the file handle directly as a parameter, so I’m passing a string value that can be used to select the appropriate data file.

Assuming the existence of directories containing json files
/data/groupA
/data/groupB
/data/groupC

And assuming a shortcode spike.html

{{ $group := ( .Get "group") }}
<p>Group: {{ $group }}</p>
{{ if eq $group "A" }}
  {{ $data := site.Data.scopeA }}

  {{ range $data }}
      <p>Item value is {{ .name }}
  {{ end }}

{{ end }}

From markdown, this works just fine
{{< spike group="A" >}}

But extend the shortcode with conditional logic:

{{ $group := ( .Get "group") }}
<p>Group: {{ $group }}</p>
{{ if eq $group "A" }}
  {{ $data := site.Data.scopeA }}
  {{ else if eq $group "B" }}
    {{ $data := site.Data.scopeB }}

  {{ range $data }}
      <p>Item value is {{ .name }}
  {{ end }}

{{ end }}

The same call doesn’t work (no error, but range has nothing to iterate over)
{{< spike group="A" >}}
but this does work
{{< spike group="B" >}}

that’s because the range statement is in the {{ else if }} block

Closing the {{ if }} block just puts $data out of scope, so this doesn’t work

{{ $group := ( .Get "group") }}

<p>Group: {{ $group }}</p>
{{ if eq $group "A" }}
  {{ $data := site.Data.scopeA }}
{{ else if eq $group "B" }}
  {{ $data := site.Data.scopeB }}
{{ end }}

{{ range $data }}
     <p>Item value is {{ .name }}
{{ end }}

It would seem that the solution would be to put $data in the file scope, outside the {{ if }} block

{{ $group := ( .Get "group") }}
{{ $data := "undefined" }}

<p>Group: {{ $group }}</p>
{{ if eq $group "A" }}
  {{ $data := site.Data.scopeA }}
{{ else if eq $group "B" }}
  {{ $data := site.Data.scopeB }}
{{ end }}

{{ range $data }}
     <p>Item value is {{ .name }}
{{ end }}

But the value assigned to $data in the {{ if }} block is forgotten at {{ end }}.

This seems a pretty straightforward use, using an input variable to set the context. Probably the solution is right there; how would you do this?

Here’s a solution:

{{ $group := ( .Get "group") }}

<p>Group: {{ $group }}</p>
{{ if eq $group "A" }}
  {{ .Scratch.Set "data" site.Data.scopeA }}
{{ else if eq $group "B" }}
  {{ .Scratch.Set "data" site.Data.scopeB }}
{{ end }}

{{ range .Scratch.Get "data" }}
     <p>Item value is {{ .name }}
{{ end }}

Are there other approaches?

You are using initialize and assign walrus operator := inside the if statement, when you really want = directly to redefine.

Change those operators inside the if statement and the redefinition should work at the end.

{{ $group := ( .Get "group") }}
{{ $data := "" }}

<p>Group: {{ $group }}</p>
{{ if eq $group "A" }}
  {{ $data = site.Data.scopeA }}
{{ else if eq $group "B" }}
  {{ $data = site.Data.scopeB }}
{{ end }}

{{ range $data }}
     <p>Item value is {{ .name }}
{{ end }}
1 Like