Counter variables and template scope

I display upcoming events like this:

{{ range where .Site.Recent "Section" "events" }}
  {{ if (ge .Date.Unix .Now.Unix) }}
    {{ template "events/li.html" . }}
  {{ end }}
{{ end }}

What I’m not sure how to do is specify what Hugo should do if there are no upcoming events. It doesn’t look like where is flexible enough to do something like:

{{ $events := where .Site.Recent "Section" "events" }}
{{ $upcoming := where Site.Recent (ge .Date.Unix .Now.Unix) true }}
{{ range intersect $upcoming $events }}
  ...
{{ end }}

If that worked I could just use len, but I can’t.

I was hoping I could get around this by using a counter variable, like:

{{ $count := 0 }}
{{ range where .Site.Recent.ByDate "Section" "events" }}
  {{ if (ge .Date.Unix .Now.Unix) }}
    {{ $count := 1 }}
    {{ template "events/li.html" . }}
  {{ end }}
{{ end }}
{{ if eq $count 0 }}
  <p>No upcoming events :(</p>
{{ end }}

But range doesn’t allow variable assignments made within its scope to survive after it ends. Even if $count is set to 1, it returns to 0 when we exit the range function.

So this doesn’t work either. Is there a solution I’ve overlooked, or is this not yet possible with Hugo?

This is a limitation with Go’s templates.

But Hugo should have a way around it. I have been nagged by this a couple times, and started to think about a patch - a kind of writeable page context that also could be used inside partials and shortcode templates used on a page.

Maybe I should pick up on that thought, as my current “Hugo hobby project” is dragging out.

1 Like

See

I saw that Scratch just got added to the master branch, but I can’t seem to get it to work:

{{ .Scratch.Set "upcoming" false }}
{{ range where .Site.Recent.ByDate "Section" "events" }}
  {{ if (ge .Date.Unix .Now.Unix) }}
    {{ .Scratch.Set "upcoming" true }}
    {{ template "events/li.html" . }}
  {{ end }}
{{ end }}
{{ .Scratch.Get "upcoming" }} {{/* prints false even if "upcoming" is set to true within the range */}}

Am I just doing something wrong?

Yes - the “dot” scope has changed. In the internal range you are using “another scratch”.

Do this:

{{ $node := . }}
{{ $node.Scratch.Set ... }}

Etc.

Scratch works, but you still have to think about scopes.

Or: You can use the magic $:

{{ $.Scratch.Set ... }} and get ...

Will update the documentation about this.

That makes sense, thanks!

Does $.Scratch doesn’t work in Shortcode templates? I am getting below error :

<$.Scratch.Add>: Scratch is not a field of struct type *hugolib.ShortcodeWithPage

In shortcode, try:

{{ .Page.Scratch.Add ... }}

Or

{{ $.Page.Scratch.Add ... }}

$ refers to the “topmost” variable in a template. In a shortcode that is ShortcodeWithPage, which contains a Page.

That worked…Thanks!!