Rendering order and how to use the scratchpad

I have a question, regards to how to use the scratchpad.
If things are rendered in order, is it possible to fill up a Scratch variable from within a renderhook, and get the result in <head> ? I’ve not been able to pull it off yet, removing 99% of the usefulness of the scratchpad as far as I’m concerned.
This below fills up the variable

{{with .Attributes.loading}}
{{ Scracth.Add “eager_images_list” (slice `<link rel=“preload” as=“image” href=“{{$medium.RelPermalink}}” imagesrcset=“{{ $smallest.RelPermalink }} {{$smallest.Width}}w{{ range uniq (slice $very_small $small $smallmedium $medium $mediumlarge $large $img) }}, {{ .RelPermalink }} {{.Width}}w{{end}}” imagesizes=“(max-width: 400px) 100vw, {{if in .Attributes.class “center”}}(max-width: 604px)100vw, 70vw{{else}}50vw, 35vw{{end}}” crossorigin=anonymous>`)}}
{{end}}

But then the questions are: in the following template, is .Content evaluated before <head> ?

<!DOCTYPE html>
<html lang="{{ default site.Language.Lang site.LanguageCode  }}"><head>
{{ partial "docs/html-head-specific" . }} > **<------ here**
{{ partialCached "docs/html-head" . }}
</head>
<body>

I want to add to preload pictures that are not lazy-loaded (nearly all are). I know someone did it already but I understand nothing at all to his code so I must do it myself.
Thanks

1 Like

I’ll try to find time to write a more complete response later, but:

  1. I use both .Store and .Scratch because neither alone achieves the desired result.
  2. You may have missed the ‘setup’ in baseof.html in

You might also want to look at @vassap 's research in

the LQIPs .Store is filled in the image partial and output in the ‘head’ partial:

which calls

HTH

And yeah the code needs to be refactored, badly, but as I keep saying: time…

So I was dead on the money on this, not a bad guess for an amateur :rofl:
But isn’t that weird to have to “assume” anything at all ? I know it’s not a characteristic of the hugo project, but it still boggles me. Why don’t developpers just document what they write when they write, when it should be the easiest ?

That is up to you.

What you need/want to do is something ala:

{{/* The partials below depends on scratch state set during content rendering, so make sure to trigger that first: *}}
{{ $tmp := .WordCount }}
{{ partial "docs/html-head-specific" . }}
{{ partialCached "docs/html-head" . }}
</head>
1 Like

Ah so the order can change !! Ok.
If only we state those choices clearly in a configuration file.
So calling upon .WordCount or anything using .Content is gonna make available, let’s say the product of render-hooks available before <body> ?
So:

  1. I trigger rendering like this
  2. two: render-link.html fills up a Scratch variable
  3. I use .Scratch.Get in <head> ?

And it would produce a predictable result ?

  1. I may be misunderstanding, but I don’t see any ordering issue in the above example; the order top down within a given template is deterministic (top down).
  2. The ordering of of different pages is performed in parallel, so the order is random.
  3. We do, however, render the languages and output formats serialised, so one trick is, if you need to collect some stuff from the rendering step, to create a output format and set a weight to make sure it gets rendered last and then pick up your scratch state there.
2 Likes

And it is me who doesn’t understand you…
I only ever needed information from .Content within the same page, nothing fancy.
Up to now I could not do something as basic as those three steps I mentioned. In a live-reload environment, the content produced would depend on which page is reloaded last, making the feature unusable. Do you want a sample ?

Please refrain from these constant jibes; it is more than a little wearing.

[OFF-TOPIC]

  • For the same reasons plumbers don’t write what they work on then they work on it.
  • For open-source projects, documentation isn’t usually the enjoyable part
  • It’s a lot harder to document as you code than you think.
    • One does not typically code everything perfectly the first time, there is often experimentation and changes. Documenting that process would be like asking a novel writer to document how they were writing their novel.
    • At the very least it interrupts the creative process.
  • Often the first time one deals with documenting code is one is in school, and one loses marks for not having perfect documentation. It is off-putting rather than helpful.
  • Documentation quickly becomes stale and out of date, and it is a never-ending chore.
  • How well do you document your templates / code?
  • For non-open-source projects (where most developers spend most of their time) the emphasis from managers is not on documentation but on getting stuff ‘out-the-door’. In the corporate world there tends to be a lack of appreciation of the value of documentation, and that affects developer habits and what developers become used to having expected of them.
  • I could go on, but I think you get the point; there are in fact many reasons, and sniping about how things aren’t the way you think they should be, is at best irritating and shows your lack of awareness (of both the realities of developing, and social).
1 Like

You have to keep in mind, in a live reload environment, .Scratch is cleared as soon as a rebuild occurs. It only gets refilled if the pages are reprocessed, which only occurs for the changed pages. You will also need to use .Store (as I have) to keep track of what you need from pages that have no changes, and therefore for which no .Scratch information is generated on a particular live rebuild.

start/end of baseof, though I haven’t seen any difference with or without this initializing:

{{ .Page.Store.Set "all_sources" (slice (slice)) }}

in single.csv:
{{ with (.Page.Store.Get “all_sources”) }}{{ . }}{{end}}

inside render-link.html, if the link is remote:

{{ .Page.Store.Add "all_sources" (slice .Destination .Text)}}

It’s supposed to list all the page’s external link. It works with .Store, but rarely does it with .Scratch in livereload environments, and never in a production environment. In that last case, the csv file is always empty with .Scratch.

An exemple of current output:

[https://www.gigalresearch.com/uk/publications-pharaohs.php egyptian scriptures https://www.ilapharm.com/stress-oxydatif-et-vieillissement-2/ the key role in the ageing process https://www.youtube.com/watch?v=-CkWmgL0k00 Then we develop a heightened healing factor https://www.drgoodfood.org/en/news/let-food-be-thy-medicine-hippocrates As Hypocrate did not say (but thought nonetheless): https://pubmed.ncbi.nlm.nih.gov/29417991/ Research shows primates to be very active during the night  https://www.sciencedirect.com/science/article/pii/S2352409X20301243 Burnt bones become fluorescent, and are very effective as a light in cellars. https://thefnc.com/research/gluten-and-dairy-are-like-addictive-drugs-to-the-brain/ terribly addictive effect https://www.pnas.org/content/106/27/10966 This article may point to an early, if short-lived, establishment of cooking and cereals http://doi.org/10.1007/s11892-013-0453-1]

ps: one long giant string, sigh… how can I set that up to get an object like this [[“url” “text”] [“url” “text”]] ? Ultimately I want something like:

{{ with (.Page.Store.Get "all_sources") }}{{ range .}}
Titre: {{ index . 0}}
URL: {{ index . 1}}
{{end}}{{end}}
1 Like

Sorry, I have lost the plot on this part of it. Could you refresh me/us on what you mean by this (that is, what are you doing, what do you expect to happen, and what actually happens)? If possible, error messages, or the actual templates, and inputs, and resulting outputs (as much as you can reasonably capture and show us).

Also DM’d you, hope you don’t mind. Didn’t want to get even more off-topic.

post edited and clarified.

If you pass a slice to .Page.Store.Add (or the Add function on any scratch instance), Hugo will append the values in the slice to any existing slice already stored in the scratch. To add a slice as-is to scratch, you need to nest it in another slice, like so:

{{ .Page.Store.Add "all_sources" (slice (slice .Destination .Text))}}

I copied your code:

{{ .Page.Store.Add “all_sources” (slice (slice .Destination .Text))}}

it does

Error: error building site: “/home/drm/WEBSITE/content/docs/History/Atlantis.md:1:1”: “/home/drm/WEBSITE/layouts/_default/_markup/render-link.html:22:10”: execute of template failed: template: _default/_markup/render-link.html:22:10: executing “_default/_markup/render-link.html” at <.Page.Store.Add>: error calling Add: cannot append slice of []interface {} to slice of interface {}

No difference with Scratch.

Yeah, that doesn’t work, sorry.

Short answer:
You should probably use a dictionary.

{{ .Page.Store.Add "all_sources" (slice (dict "destination" .Destination "text" .Text))}}

If you have only two values per render hook, and one of them is a string which will be unique per page, you can use .Store.SetInMap instead.

<!-- In your render hook: -->
{{ .Page.Store.SetInMap "all_sources" .Destination .Text }}
<!-- Output your sources -->
{{ range $dest, $title := (.Store.Get "all_sources")}}
Title: {{ $title }}
URL: {{ $dest }}
{{end}}

If you want to use nested slices, you’ll need to add a slice within a slice the first time you call .Store.Add, and then add a normal slice in all subsequent instances.

{{ if (.Scratch.Get "all-sources" }}
    <!-- Value already exixts -->
    {{ .Scratch.Add (slice .Destination .Text) }}
{{ else }}
    <!-- Value doesn't exist yet -->
    {{ .Scratch.Add (slice (slice .Destination .Text)) }}
{{ end }}

Longer answer:

The following comes from reading the source code for the relevant functions. I’m not a Go programmer, so it might not be correct, but it matches the behaviour I can see.

So the reason why my first suggestion didn’t work (and it probably requires a clarification in the docs, I’ll see if I can adjust it), is because .Scratch.Add includes a bit of special handling to support both raw values and slices. Namely, it checks if the value exists, and whether the existing value is a slice. If it’s a slice, it runs the Append function, if it’s not, it just adds the values together (so either addition or concatenation depending on type).

The Append function also includes a bunch of special handling, as it can be used in two different ways: one or more values can be added to a slice, or two slices can be merged together. Since Go is statically typed, each of the values in the slice must be of the same type. The Append function performs the following checks to determine how it operates:

  1. Is the target nil?
  2. Is the target a slice?
  3. Is the target a slice which contains other slices?
  4. Is there exactly one other argument to the function?
    a. Is that one argument a slice?

If the target is nil, the function returns a slice made of the arguments. If the target is not a slice, the function throws an error. If the target does not contain other slices, and the single argument is a slice, then the values in the slice are appended.

If the target is a slice which contains other slices (the third check), then we get some of that special handling. The Append function will check whether all other arguments passed to the function are also slices of a type compatible with the slices already in the target slice, and if they are, then it will append the slices directly to the target as it would any other raw value. It’s a sensible behaviour, but it’s not currently documented anywhere.

Your output code causes an error I’ve never seen yet:

Error: “/home/drm/WEBSITE/layouts/_default/single.csv:2:1”: parse failed: template: _default/single.csv:2: too many declarations in command

Same for both storage instructions.
So I knew the scratchpad was broken ! Good luck with that. What I don’t understand is, if Go has static typing, why is the templating language any different ? That would make things much easier for both the developer and user.
Wait, it’s not clear at all to me what typing it has :face_with_raised_eyebrow:
If it was static, could we get a map of all the types so knowing what to convert in what and how would be a piece of cake ?

That’s on me, sorry, that line should be:

{{ range $dest, $title := (.Store.Get "all_sources")}}

I’ve updated it above

1 Like

Not sure how you inferred that from what I wrote.

I said that in my reply.

It isn’t.

What?

Append does appropriate type checks all over the place. The only complexity is beacuse it can be called with two different parameter styles, and one particular case was not in the documentation (although that case is handled by Hugo in a perfectly intuitive manner).

If you need to check whether a value is a slice or a map in your templates, you can pass it to reflect.IsSlice or reflect.IsMap

Ok, I think I’m content not to understand any better, I’ll just hate that language and keep it at that.
But I thank you for your code, I can finally get the list of references that I wanted :+1: :smiling_face_with_tear:

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.