Scope variable
Hugo template has the limitation of scope variable, where you can't set a outer variable from a local scope. This is actually a limitation of Go Template.
A variable's scope extends to the "end" action of the control structure ("if", "with", or "range") in which it is declared, or to the end of the template if there is no such control structure. A template invocation does not inherit variables from the point of its invocation.
You might expect outerResult=true;
for the following example, but the correct answer is outerResult=false;
as the setting of variable within the if
scope is a local scope and doesn't affect variable in the outer scope.
{{ $result := false }}
{{ if eq $result false }}
{{ $result := true }}
{{/* innerResult=true; */}}
innerResult={{ $result }};
{{ end }}
{{/* outerResult=false; */}}
outerResult={{ $result }};
With scope variable limitation, you can't count variable within a range
loop as well. For the following example, innerCount
will always be 1 (each loop is a new local scope) and outerCount
is 0. innerCount
can access $count
variable of the outer scope, but it can't change it from the inner scope.
{{ $count := 0 }}
{{ range .Site.Pages }}
{{ $count := add $count 1 }}
{{/* innerCount=1; */}}
innerCount={{ $count }};
{{ end }}
{{/* outerCount=0; */}}
outerCount={{ $count }};
Use .Scratch
.Scratch acts as a “scratchpad” to allow for writable page-scoped or shortcode-scoped variables.
Both innerResult
and outerResult
will return true as expected based on the logic flow.
{{ $.Scratch.Set "result" false }}
{{ if eq ($.Scratch.Get "result") false }}
{{ $.Scratch.Set "result" true }}
{{/* innerResult=true; */}}
innerResult={{ $.Scratch.Get "result" }};
{{ end }}
{{/* innerResult=true; */}}
outerResult={{ $.Scratch.Get "result" }};
count
will increment correctly for the following case.
{{ $.Scratch.Set "count" 0 }}
{{ range .Site.Pages }}
{{ $.Scratch.Add "count" 1 }}
innerCount={{ $.Scratch.Get "count" }};
{{ end }}
outerCount={{ $.Scratch.Get "count" }};