Optional shortcode parameters & conditional variables assignment

I’m using Hugo v0.29. I do conditional variable assignments in my shortcodes based on whether an optional shortcode parameter was provided. I have found that Hugo doesn’t behave consistently for first ($0) and second ($1) shortcode parameter evaluations.

The following code, using the cond function, produces the correct output and no runtime errors when the shortcode is called with or without parameters (i.e., w/wo Param $0):

{{ $var := (cond (isset $.Params 0) (.Get 0) "No Param $0") }}
$var = "{{- $var -}}"

However, the same code but applied to the second parameter ($1) produces a “No shortcode param at .Get 1 in page …, have params:” runtime error when the shortcode is called with only one parameter (i.e., without Param $1), even though the output is rendered correctly:

{{ $var := (cond (isset $.Params 1) (.Get 1) "No Param $1") }}
$var = "{{- $var -}}"

==>
(a) The behavior is different when evaluating the first ($0) and second ($1) parameter, which is confusing.
(b) For the second-parameter ($1) code, the error is produced for the .Get 1 call in the true clause of the cond function, even though this clause is not executed when the shortcode is called without a second parameter (because the isset $.Params 1 condition correctly evaluates to false). --> It seems that Hugo validates the true clause even when the condition evaluates to false, which defeats the purpose of the conditional logic.
Again, the same code for the first-parameter ($0) doesn’t produce an error, and in both cases the output is good both when the shortcode is called with and without the relevant parameter.

My current solution is to use Scratch for parameter $1 evaluations, but I feel this unnecessarily complicates the code:

{{ $.Scratch.Set "var" "default value" }}
{{ if (isset $.Params 1) }}{{ $.Scratch.Set "var" (.Get 1) }}{{ end }}
var = {{ $.Scratch.Get "var" }}

Is there a simpler way to do a conditional variable assignment based on whether parameter $1 was passed to the shortcode?

I also noticed other related differences for parameter $0 and $1 evaluations. For example, when the following shortcode is called without a first parameter ($0), there’s no runtime error, the if $var condition evaluates to false, and the else clause is executed:

{{ $var := .Get 0 }}
{{ if $var }}$var = {{- $var -}}{{ else }}$var is not set{{end }}

However, if you change .Get 0 to .Get 1 and call the shortcode without a second parameter ($1), (a) The .Get 1 call in the variable assignment produces a runtime error; (b) The condition evaluates to true, which produces “error: index out of range for positional param at position 1” in the output for the $var reference in the if clause.
I can understand why this code might not be valid, but then it shouldn’t work for the first parameter ($0) either?!

Okay so I was just about to open a new topic about the very same subject. I’ll post here instead.

Hi, I’d like to have optional params in my shortcode. However, when I process this with cond, I’m getting some errors :

{{ $width := cond (gt (len .Params) 1) (.Get 1) "512" }}
{{ $quality := cond (eq (len .Params) 3) (.Get 2) "30" }}

Returns:

ERROR 2018/03/28 14:51:23 No shortcode param at .Get 1 in page posts/2018/01/projection-de-conquerir-notre-9346.fr.md, have params: [BXv4Txr9CYU]
ERROR 2018/03/28 14:51:23 No shortcode param at .Get 2 in page posts/2018/01/projection-de-conquerir-notre-9346.fr.md, have params: [BXv4Txr9CYU]

I guess that’s normal because the content of .Get 1 will be evaluated before being passed to the function. The same goes with the default function.

Is there some trick I’m unaware of, or is the following snippet the most convenient/elegant way to do the trick? My template now:

{ if eq (len .Params) 2 }}{{ .Scratch.Set "width" (.Get 1) }}{{ else }}{{ .Scratch.Set "width" "512" }}{{ end }}
{{ if eq (len .Params) 3 }}{{ .Scratch.Set "quality" (.Get 2) }}{{ else }}{{ .Scratch.Set "quality" "30" }}{{ end }}
{{ $width := .Scratch.Get "width" }}
{{ $quality := .Scratch.Get "quality" }}

I’ve found at least two other topics talking about this issue:

I think the problem is that named parameters when queried by .Get return "" if they don’t exist, while positional parameters raise an error instantly.

If everybody agrees positional params should return an empty value too (so that they can be chained with with or if to check for their existence), I will be glad to make a PR to fix this. @bep, what do you think?

1 Like

I’d like to have optional params in my shortcode

I use something like this:

{{- .Scratch.Set `theIcon` `fas fa-question` }}
{{- .Scratch.Set `theSize` `fa-1x`           }}
{{- if .IsNamedParams }}
  {{- .Scratch.Set `theIcon` (.Get `name`  | default (.Scratch.Get `theIcon`) ) }}
  {{- .Scratch.Set `theSize` (.Get `size`  | default (.Scratch.Get `theSize`) ) }}
{{- else}}
  {{- $myPos := 0}} {{ if gt (len .Params) $myPos }} {{.Scratch.Set `theIcon` (.Get $myPos) }} {{end}}
  {{- $myPos := 1}} {{ if gt (len .Params) $myPos }} {{.Scratch.Set `theSize` (.Get $myPos) }} {{end}}
{{- end}}
  • first set the defaults
  • allow named params
  • if positional - use the position integer just once per param
  • yes it is quite “noisy”

if everybody agrees positional params should return an empty value too

Since

{{ index (seq 1 2 3) 5 }}

does not raise an error as well - it could make sense to return an empty value.

I think you are right, but you need to create a GitHub issue.

This was merged earlier today and should be released with hugo v0.40. Thank you so much it will just simplify things <3

@cmal Can you point to the relevant GitHub PR? Thanks.

1 Like