Hugo

Access Scratch inside Shortcode

I have the following code in my list.html:

{{ range (.Paginate .Pages).Pages.ByPublishDate }}
<div class="section">
    {{.Scratch.Set "ShownInList" true}}
    {{ partial "post.html" . }}
    {{ .Scratch.Delete "ShownInList" }}
</div>
{{ end }}

The partial post.html renders the content.

In my content I use a shortcode that’s defined as follows:

{{ if .Page.Scratch.Get "ShownInList" }}
    SOMETHING
{{ else }}
    SOME OTHER THING
{{end}}

The shortcode always renders SOME OTHER THING. The .Scratch is always empty.

(I use the same logic inside post.html where it does work.)

The documentation says:

Note that .Scratch from a shortcode will return the shortcode’s Scratch , which in most cases is what you want. If you want to store it in the page scoped Scratch, then use .Page.Scratch .

What am I missing?

When running hugo instead of the server the culprit was shown on the command line:

Error: Error building site: “[…]”: failed to render shortcode “image”: failed to process shortcode: “[…]/layouts/shortcodes/image.html:3:20”: execute of template failed: template: shortcodes/image.html:3:20: executing “shortcodes/image.html” at <.Date.Day>: can’t evaluate field Date in type *hugolib.ShortcodeWithPage

I had tried to access .Date.Day instead of .Page.Date.Day.

The hugo server hadn’t shown an error. Maybe you could add one to the output there as well?

I think there’s a bug hidden somewhere in the shortcuts and .Scratch implementation. Without changing anything, just by clicking around on the webpage it stopped working.

I’m using the same logic in the shortcut and in post.html.

If I change something in the shortcode it affects the logic in post.html. Both read the .Scratch but don’t alter it.

(I had added <p>Scratch: {{ .Page.Scratch.Get "ShownInList" }}</p> at the beginning of the shortcode to help me debug. I removed the .Page to see what happens. Then the .Scratch logic in post.html logic stopped working.)

You should be able to use .Scratch.Set to set a variable in a template and then use .Scratch.Get to access that variable in a shortcode. I’m running 0.55.6 and that works (at least where .Content is processed).

Does your call to the shortcode appear below Front Matter? Is “SOME OTHER THING” appearing when you view the page rendered by list.html?

Hi @marcia, thanks for your reply.

Yes, the call appears below Front Matter and “SOME OTHER THING” (or the code used in there) is shown on the list page.

I have to store images with the .md file. This shortcode is supposed to reference the correct file on both the list and the single page view:

<figure class="image">
{{ if .Page.Scratch.Get "ShownInList" }}
    {{ $day := .Page.Date.Day }}
    <img src="{{ $day }}/{{.Get 0}}">
{{ else }}
    <img src="{{.Get 0}}">
{{end}}
</figure>

@dominik.mayer using scratch to do “inter template” communication is … hard at best.

What happens in your situation, I guess, is that the content (and shortcode) is rendered before .Scratch.Set, but not always. Which is the odd behaviour you see (this is parallel processing in play).

What you want to do isn’t possible, I think. The .Content is rendered once (per output format, language).

Thank you @bep, that explains. I guess it’s the race condition with the shortcode. A similar code inside a partial works consistently:

list.html

{{ range (.Paginate .Pages).Pages.ByPublishDate }}
<div class="section">
    {{.Scratch.Set "ShownInList" true}}
    {{ partial "post.html" . }}
    {{ .Scratch.Delete "ShownInList" }}
</div>
{{ end }}

post.html

{{ if .Scratch.Get "ShownInList" }}
    {{ $day := printf "%s" (.Date.Format "02") }}
    {{ range .Params.images }}
        <img src="{{ $day }}/{{.}}">
    {{ end }}
{{ else }}
    {{ range .Params.images }}
        <img src="{{.}}">
    {{ end }}
{{end}}

Is there any better way of knowing if the current code is rendered on a list or a single page?