Variable Scope Problem In Template Override Partial

I’ve got a page for my contact form, and I want to add the jQuery library and a custom javascript file to that page only.

My theme has a partial called head-additions.html, which conveniently is an empty file and is loaded in the theme’s baseof.html right before the closing “head” tag with the following:

{{ block "head" . }}{{ partial "head-additions.html" }}{{ end }}

That seemed like the perfect opportunity to add the two script files without overriding a theme file that might get changed with a future update.

I created a /layouts/partials/head-additions.html to override the theme version, and added the following code:

{{ range .Params.js }}
<script src="/static/js/{{ .Params.js }}"></script>
{{ end }}

My intent is that I can add the two javascript files in the frontmatter of the like this:


  • jquery-3.5.1.min.js
  • custom.js

Unfortunately, the function in my head-additions.html isn’t getting passed the .Params. I can output hard-coded text and it shows up on the site, so I know my template override is working.

I looked at some of the other partials included in the theme files, and they mostly have a period after the file name, which I thought was required to pass the page variables, but the head-additions.html does not have it. Although I don’t intend to override the baseof.html, I experimented by adding the period and it gave me an error:

Failed to render pages: render of “page” failed: execute of template failed: template: page/single.html:45:24: executing “head” at <partial “head-additions.html” .>: error calling partial: “/home/username/Hugo/foldername/layouts/partials/head-additions.html:2:23”: execute of template failed: template: partials/head-additions.html:2:23: executing “partials/head-additions.html” at <.Params.js>: can’t evaluate field Params in type string

What am I doing wrong?

Could you try:

{{ with .Params.js }}
  {{ range . }}
    <script src="/static/js/{{ . }}"></script>
  {{ end }}
{{ end }}

Hi @sephore,

Thanks for the quick reply. That works if I add the period to the line in baseof.html to put the .Params in scope:

{{ block "head" . }}{{ partial "head-additions.html" . }}{{ end }} 

However, without the period, I don’t get anything.

Since I really don’t want to override baseof.html, I looked around and found another theme partial called “site-scripts.html” which puts scripts in the footer. The .Params are in-scope in that file.

With your code, I can get the scripts to display, so it appeared I had two problems:

  1. .Params wasn’t in-scope in my head-additions.html because the period wasn’t there in baseof.html
  2. For some reason, my code wasn’t working.

Can you explain why the following wasn’t working:

{{ range .Params.tags }}

Tbh, I didn’t notice the missing dot. That’s problematic and you should read this to understand why.

Also, since the .Params.js is only available at the contact page, any other page where that code is executed will fail to render since… well, .Params.js is absent on them.

with is one way to verify if the variable is available and the code block can be executed.

I switched to overriding another partial, “site-scripts.html”, which adds its content right before the closing body tag and is short enough that I don’t mind overriding it. That will save me adding the period to the partial declaration in baseof.html.

Unfortunately, I spoke too soon. Even though the only page with the “js” in the frontmatter was the contact page, the javascript was showing up on every page.

To simplify things I switched my frontmatter on the contact page to:

title: "Contact"
date: 2020-08-28T15:27:10-07:00
draft: false
menu: main
addcustomjs: true

Next, I went with the following in the site-scripts.html file to check for the presence of “addcustomjs”:

{{ with .Params.addcustomjs }}
    <script src="/static/js/jquery-3.5.1.min.js"></script>
{{ end }}

Now the jquery file is still loading on every page.

I know I’m missing something obvious here. You’ve helped me solve the scope problem. Should I start another thread for the issue with restricting the output only to the Contact page?

I don’t know, this issue could still be related to the scope. It seems you’re avoiding the dot…? (Or I misread something.)

It would help if you could share a repository, otherwise it’s a guessing game for us.

I’m not avoiding the dot, I’m avoiding overriding the baseof.html file of the theme I’m using so that I don’t complicate things down the line if I want to update the theme. It is there that the dot wasn’t used for the /layouts/_default/baseof.html file’s call of the partial /layouts/partials/head-additions.html.

I don’t have a repo I can share as I’ve just been testing Hugo locally, but I’m using the Ananke theme, which you can find on Github.

As noted above, I’m just overriding the /layouts/partials/site-scripts.html file from Ananke.

I can create a dummy repo on Github if it will help, though I probably won’t be able to do that until tomorrow…

Right. Thanks for sharing more info.

I think I know the issue…

To understand why, see partialCached.

@sephore, you are my hero.

The moment you mentioned caching, I knew you were correct, because it fits the apparently inconsistent results I was seeing.

I broke down and overrode the theme’s /layouts/_default/baseof.html with my own, and made two changes. First, I added the period to the head-additions.html so it looks like this:

{{ block "head" . }}{{ partial "head-additions.html" . }}{{ end }}

and now I can access variables there as needed.

Next, I changed the “partialCached” to “partial” for the site-scripts.html declaration:

{{ block "scripts" . }}{{ partial "site-scripts.html" . }}{{ end }}

I tested it, and now my code is working as I expected.

Thank you very much for helping me figure it out.

1 Like

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