Calling variables inside Hugo partial - go

I have this code I use inside a partial in Hugo to pass context to it.
{{- $ctx := . -}}
{{- $curPage := .page -}}
{{- $otherVar := .otherVar -}}
{{- with $curPage -}}
{{ $section := .CurrentSection }}
{{ if .IsHome }}
<span class="post-section">{{ $section.Title }}</span>
{{ else }}
{{ $section.Title }}
{{ end }}
{{- end -}}
I then add {{- $curPage := . -}} at the top of the template where I want the partial to appear, then call the partial as {{ partial "partial-name.html" (dict "page" $curPage "otherVar" .) }}. However, the content returns nil on the homepage while it works everywhere else sitewide. Could anyone look at my code and tell me where I went wrong?

Sorry - didn't see your with statement.
So {{- $curPage := .page -}}
is a typo.
.Page
Tested on local - most present version of hugo.
Also note - I don't think homepage has a section so your span will output very little as most of the currentsection or section related will return nothing.

Since you call the partial like this:
{{ partial "partial-name.html" (dict "page" $curPage "otherVar" .) }}
^^^^^^^^^^^^
Notice
The dot (.) is contained in .otherVar. To find out if you are on the home page, use something simple like this at the top of partial-name.html:
{{ if .otherVar.IsHome }}
<pre>Debugging: YES .IsHome</pre>
{{ else }}
<pre>Debugging: NOT .IsHome</pre>
{{ end }}
After you test with the above GO HTML fragment, you can update your original code above.
TIP: In the Hugo world, it is common to use "context", "ctx", "page", or "Page" rather than "otherVar" as the name of the dictionary key that contains the dot. For a discussion about this, see Naming convention for page context (within a dictionary) when calling partial: seeking opinions in the Hugo discussion group.
ANOTHER TIP: There are some Hugo weirdnesses related to case sensitivity so I would not use "otherVar" anyway. Instead use "othervar", "context", or any name that is all lower case with no whitespace. I do this because I have spent a lot of time messing around with case sensitivity Hugo issues.

Related

Changing a html class after the first item in a Hugo loop

So I am making a review carousel using Bootstrap and Hugo, I've got code that breaks down into this:
{{ range seq 1 3 (len site.Data.reviews) }}
...
{{ range seq . (add . 2) }}
{{ with (index site.Data.reviews (string .)) }}
{{ .des }}
{{ end }}
{{ end }}
...
{{ end }}
So there's two loops, one to make the slides for the carousel, and the other to fill the slides with data files. The issue is I need to delete the active class and adjust the data-bs-interval input on the next few slides I thought about making an if statement but I'm not sure how to replace the first div with one that doesn't have the active class after that in whats generated.
I don't know if this is the best solution to it, instead of editing the loop I wrote a bit of javascript:
var addActive = document.getElementById('carouselExampleDark').getElementsByClassName('carousel-item')[0];
addActive.classList.add("active");
That works for my use case so I'll leave it at that.

Hugo data files from dynamic parameter

I'm developing a big hugo template. I try to simplfy the problem, so I have two datafile:
PROMO_00_1.yaml
PROMO_00_2.yaml
that are phisically stored in this directory:
themes/data/hp/
So, in the site config the user will decide which of this data file will be used simply indicate it in a param (HpElement).
In the template I call the partial in this way:
{{ partial "multiplepages/homepage/promos/00_promo_singleslide_text_video" (dict "context" . "data" $.Site.Params.HpElement) }}
In a partial I write:
{{ $data_partial := (printf "$.Site.Data.homepage.%s" .data)}}
{{ $data_partial}}
and the Hugo output is on the website:
$.Site.Data.homepage.PROMO_00_1
What I need is to access the single variable inside the .yaml file but the user MUST can decide which YAML file have to use. How can I achieve that?
Thanks
I just finished up a similar use case where I needed to select a YAML based on a date stamp. The index function is key to this operation.
https://gohugo.io/functions/index-function/
Here is a simplified version of my situation:
{{ $date := now.Format "s20060102"}}
{{ $data := (index .Site.Data.schedule $date) }}
{{ with $data }}
<h1>.longdate</h1>
{{ range .times }}
<h2>{{ .name }} - {{ .location }}
{{ end}
{{ end}
The example in the Hugo documentation uses an example where the data file is selected based on an attribute in the front matter.

How to declare a global variable in a template?

I need to write a template where I first define some variables, and then use them in what will be generated from the template:
{{ if $value.Env.CADDY_URL }}
{{ $url := $value.Env.CADDY_URL }}
{{ else }}
{{ $url := printf "http://%s.example.info" $value.Name }}
{{ end }}
{{/* more template */}}
{{/* and here I would like to use $url defined above */}}
{{ $url }}
I get the error
undefined variable "$url"
Reading the documentation, I see that
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.
Does this mean that there are no global (or scoped on the whole template) variables? Or is there a way to define $url so that it can be reused later in the template?
Variables are scoped. You create the $url variable inside the {{if}} and {{else}} blocks, so they are not visible outside of those blocks.
Create the variable before the {{if}}, and use assignment = instead of declaration :=:
{{$url := ""}}
{{ if . }}
{{ $url = "http://true.com" }}
{{ else }}
{{ $url = "http://false.com" }}
{{ end }}
{{ $url }}
Testing it:
t := template.Must(template.New("").Parse(src))
fmt.Println(t.Execute(os.Stdout, true))
fmt.Println(t.Execute(os.Stdout, false))
Output (try it on the Go Playground):
http://true.com<nil>
http://false.com<nil>
Note: Modifying template variables with assignment was added in Go 1.11, so you need to build your app with Go 1.11 or newer. If you're using an older version, you cannot modify values of template variables.
Edit: I've found a duplicate: How do I assign variables within a conditional
You can mimic "changeable template variables" in earlier versions, see this question for examples: In a Go template range loop, are variables declared outside the loop reset on each iteration?

Combining two if conditions into one

The below works
{{- if hasKey (index $envAll.Values.policy) "type" }}
{{- if has "two-wheeler" (index $envAll.Values.policy "type") }}
<code goes here>
{{- end }}
{{- end }}
while the below fails with "runtime error: invalid memory address or nil pointer dereference"
{{- if and (hasKey (index $envAll.Values.policy) "type") (has "two-wheeler" (index $envAll.Values.policy "type")) }}
<code goes here>
{{- end}}
There is no list by name "type" declared under $envAll.Values.policy.
In Go, if the right operand is evaluated conditionally, why does the last condition gets evaluated in the second code snippet? How do I solve it?
Edit (since it marked as duplicate):
Unfortunately, I cannot use embedded {{ if }} like it is mentioned in the other post.
I simplified my problem above. I actually have to achieve this...
{{if or (and (condition A) (condition B)) (condition C)) }}
<code goes here>
{{ end }}
You get an error when using the and function because the and function in Go templates is not short-circuit evaluated (unlike the && operator in Go), all its arguments are evaluated always. Read more about it here: Golang template and testing for Valid fields
So you have to use embedded {{if}} actions so the 2nd argument is only evaluated if the first is also true.
You edited the question and stated that your actual problem is this:
{{if or (and (condition A) (condition B)) (condition C)) }}
<code goes here>
{{ end }}
This is how you can do it in templates only:
{{ $result := false }}
{{ if (conddition A )}}
{{ if (condition B) }}
{{ $result = true }}
{{ end }}
{{ end }}
{{ if or $result (condition C) }}
<code goes here>
{{ end }}
Another option is to pass the result of that logic to the template as a parameter.
If you can't or don't know the result before calling the template, yet another option is to register a custom function, and call this custom function from the template, and you can do short-circuit evaluation in Go code. For an example, see How to calculate something in html/template.
You could potentially use the Helm default function to avoid the second conditional.
It looks like your code is trying to test if .Values.policy.type.two-wheeler is present, with the caveat that the type layer may not exist at all. If there's no type, then .Values.policy.type evaluates to nil, and you can't do additional lookups in it.
The workaround, then, is to use default to substitute an empty dictionary for nil. Since it's then a dictionary you can do lookups in it, and since the default is empty testing for any specific thing will fail.
{{- $type := $envAll.Values.policy.type | default dict }}
{{- if has "two-wheeler" $type }}
<code goes here>
{{- end }}
You can put this into a one-liner if you want
{{- if has "two-wheeler" ($envAll.Values.policy.type | default dict) }}...{{ end }}
If you're actually going to use the value and not just test for its presence, another useful trick here can be to use the standard template with block instead of if. If with's conditional is "true" then it evaluates the block with . set to its value, and otherwise it skips the block (or runs an else block). In particular here, if a map value isn't present, then its lookup returns nil, which is "false" for conditional purposes (though note other things like 0 and empty string are "false" as well).
{{- $type := $envAll.Values.policy.type | default dict }}
{{- with $type.two-wheeler }}
{{-/* this references .Values.policy.type.two-wheeler.frontWheel */}}
frontWheel: {{ .frontWheel }}
backWheel: {{ .backWheel }}
{{- end }}

HUGO IN function not accepting variable as ITEM

I am using the IN function of Hugo that can be used as in SET ITEM as defined at Hugo Documentation for IN
I am trying to filter out posts of a particular category by the below code
{{ range where .Site.Pages "Section" "products"}}
{{ if in .Params.categories "New Arrival" }}
<li>{{ .Title }}</li>
{{end}}
{{end}}
the above code works completely fine.
But if I use a variable instead of a string for the ITEM. It always returns False.
{{ $display_product_cat := "New Arrival" }}
{{ range where .Site.Pages "Section" "products"}}
{{ if in .Params.categories $display_product_cat }}
<li>{{ .Title }}</li>
{{end}}
{{end}}
The above code does not work as expected.
Wondering if I am missing something here. Appreciate your time and help.
You can test what the boolean is by something like:
{{ $i := in .Params.categories "New Arrival" }}
{{ $i }} ## will output true|false
{{ $var := "New Arrival" }}
{{ $j := in .Params.categories $var }}
{{ $j }} ## will output true|false
If it's giving false outcomes, you might need to inspect how your .Params are actually constructed (what pattern are they in, etc.).
Providing more info will allow a better debug for you (like the snippet of your Params).

Resources