String / Variable Concatenation to build Path for partial


#1

Hello all,

I’m trying to use Hugo to generate pages dynamically building from some partials as defined by an array defined in the front matter:

+++
Description = "A demo page"
menu = "main"
components = [
  "a",
  "b",
  "c"
]
+++

However I don’t want the user to have to put in the full path to each partial in the components array, so I’d like to manipulate that in the single template to prefix with a path then grab the relevant component, so something like the below, however you I can’t find the correct way to concatenate vars / strings. A broken example using a +:

{{ range $index, $component := .Params.components}}
  {{ $path := "components/" + $component}}
  {{ partial $path . }}
{{ end }}

I’ve had a good look around, but can’t find a clear answer, to create a new variable by concatenating. Any ideas - or should I look to find another solution?

Many thanks!


Partials from md file with unique variables for each
#2

You can use printf:

https://github.com/bep/bepsays.com/blob/master/layouts/partials/menu.ace#L20

The example looks weird, but works. Search for printf in this doc:
http://golang.org/pkg/text/template/

Also, if you want you use $pathoutside of the range – in the upcoming Huog release (or build from source), there is Scratch:

https://github.com/spf13/hugo/blob/master/docs/content/extras/scratch.md


#3

Perfect, thanks @bjornerik !

For the record, the following worked for my case:

{{ range $index, $component := .Params.components }}
  {{ $path := $component | printf "%s/%s" $.Site.Params.componentdir | printf "%s" }}
  {{ partial $path }}
{{ end }}

Thanks again!


.Site.Data.lang.en shows up as string, expected as code
#4

I’ve encountered a further problem with the solution above, I wasn’t initially passing the context with a . after the partial $path when I try to do this, it fails.

I’ve tried wrapping the path in quotes to ensure it’s a string, but that’s not necessary and then breaks the string concatenation to the partials/ folder.

When I manually specify a path as a hardcoded string and pass the context with the dot it works. There’s something I suspect about the order of execution here that’s stopping me passing the context.

So the above code snippet with:
{{ partial $path . }} in the range will error with the following if the partial specified by $path is trying to access the {{ .Title }}, which is defined:

ERROR: 2015/04/27 template: theme/partials/components/header/header-header_demo.html:2:21: executing "theme/partials/components/header/header-header_demo.html" at <.Title>: can't evaluate field Title in type string in theme/partials/components/header/header-header_demo.html

Any ideas?


#5

In your case the “.” is $component = string.

{{ partial $path $. }}

Or similar should work.


#6

Ah, got you, unfortunately $. didn’t work to access the parent context, but just caching the . context outside of the range loop and using that, has done the trick.

Thank you for the prompt response!


#7

Every way Ive tried this, I cannot get the actual partial to execute.
I have an array of partials in my sites config, I will be pulling in different partials for different sites.
This code:
{{ range $index, $component := $.Site.Params.components }} {{ $widget := $component | printf "{{ partial %q" | printf "%s . }}" }} {{ $widget }} {{ end }}
Literally displays this on the site. Instead of executing and displaying my partial.
{{ partial “about_me.html” . }}

Ive also tried this, which is more of what @oller suggested.
{{ range $index, $component := $.Site.Params.components }} {{ $path := $component | printf "%s/%s" $.Site.Params.componentdir | printf "%s" }} {{ $path }} {{ end }}
But then it just displays:
widgets/about_me.html

Any help would be appreciated! I feel like its just a small Go syntax that Im missing or doing wrong…
Thanks!


#8

Yea, that doesn’t wok … here is a totally untested from me:

{{ $widget := (partial (printf "%s%s" $.Site.Params.componentdir $component  )) }}

#9

Thanks @bep!
Just had to the “/” in between the %s’s.

Working code:
{{ range $index, $component := $.Site.Params.components }} {{ $widget := (partial (printf "%s/%s" $.Site.Params.componentdir $component )) }} {{ $widget }} {{ end }}

The global Params within those partials are not working now though :confused: Ill have to read up on how to get those to call properly.


#10

**Update
I modified my config to just include that “/”, keeping the code cleaner.
{{ range $index, $component := $.Site.Params.components }} {{ $widget := (partial (printf "%s%s" $.Site.Params.componentdir $component )) }} {{ $widget }} {{ end }}


#11

Hello,

I have an issue to something related.

in page.md

---
customdata: test
---

in the data/test.yml

hero:
  title: Super Hero
contentsection:
  title: Some nice content

and in the template file

{{ $data := index .Site.Data .Params.customdata }}
{{ range $index, $element := $data}}
<div>
  {{ $widget := (partial (printf "%s%s" "fc/" $index )) }}
  {{ $widget }}
</div>
{{ end}}

with partials named fc/hero.html and fc/contentsection.html

All nice and good, but i want to pass $elemen to the $widget, and access {{ .title }} in the partial file. Any ideas here?


#12

Hello, please see here on how to format code blocks in Markdown for this forum: Sharing code in the forums


#13

Done. Can you please help me?


#14

It’s difficult to provide proper help if you don’t share the source of your site (See Requesting Help).

But even then, see if this helps… you are missing passing any context to the partial. See https://gohugo.io/templates/partials/

From there:

One of the most common mistakes with new Hugo users is failing to pass a context to the partial call. In the pattern above, note how “the dot” (.) is required as the second argument to give the partial context. You can read more about “the dot” in the Hugo templating introduction.

The “dot” passes the current context. If you want to pass more stuff in addition to that, you can pass a dict to the partial that includes the “dot” too.

Here’s a simple example of using dict for passing stuff to a partial.

The concepts of the context and dot and dict might to too many new things to learn… so take your time to read through the docs. Feel free to ask more questions if and as you find trouble following the documentation. The Hugo community is working hard to improve the documentation based on user feedback.


#15

Thanks for the support, i found the issue, hope this helps others.
Passing the context to a partial.

this doesn’t work

{{ $data := index .Site.Data.landing .Params.cdata }}
{{ range $index, $element := $data}}
   {{ $widget := (partial (printf "%s%s" "fc/" $index )) }}
   {{ $widget . }}
{{ end}}

ERROR 2017/12/23 22:23:15 Error while rendering "page": template: /.../layouts/landing/flexible-content.html:7:11: executing "main" at <$widget>: can't give argument to non-function $widget

this works

{{ $data := index .Site.Data.landing .Params.cdata }}
{{ range $index, $element := $data}}
    {{ $widget := (printf "%s%s" "fc/" $index ) }}
    {{ partial $widget . }}
{{ end}}