MusikPolice
MusikPolice

Reputation: 1749

Hugo: Combining Data Ranges and shortcodes

Hugo has a YouTube shortcode. I'd like to use it in conjunction with data templates to show a list of videos that is driven by the contents of a JSON file.

Ideally, in site/content/index.md, I'd have something like this:

# Some YouTube videos
{{ range $.Site.Data.youtube.videos }}
    {{< youtube .id >}}
{{ end }}

and in site/data/youtube.yml, I'd have something like this:

---
videos:
- id: abc123
- id: xyz456

so that each of the video ids that I list in my .yml file is rendered as an embedded YouTube video on my homepage.

The problem here is that I can't seem to use hugo functions like range in .md files. When I try this, the first code snippet just gets rendered as text, rather than being replaced with YouTube videos:

Can't use data ranges in content files

Fair enough, let's extract the code into a partial that lives in site/layouts/partials/youtube-list.html:

{{ range $.Site.Data.youtube.videos }}
    {{< youtube .id >}}
{{ end }} 

Then we'll modify site/content/index.md to reference the partial:

# Some YouTube Videos
{{ partial "youtube-list.html" . }}

Now, when I try to run the server, Hugo tells me that I can't use the < character in a partial:

ERROR 2018/01/05 11:30:33 partials/youtube-list.html : template: partials/youtube-list.html:2: unexpected "<" in command

Great. I suppose I could create a custom shortcode instead, but they appear to have the same limitation - a shortcode cannot reference another shortcode:

ERROR 2018/01/05 11:32:39 shortcodes/youtube-list.html : template: shortcodes/youtube-list.html:3: unexpected "<" in command

I guess I could find the source for the YouTube shortcode in Hugo's GitHub repository and "borrow" it wholesale for use in my new shortcode, but that seems like unnecessary duplication of code.

tl;dr: Is there any way to combine data templates and shortcodes in Hugo so that the same shortcode is used to render each element in a list that is read from a data template?

Upvotes: 4

Views: 3986

Answers (2)

7m45h
7m45h

Reputation: 59

may be use Inline Shortcodes :)


<!-- post.md -->

{{< my_shortcode.inline >}}

<pre>
{{ range $index, $element := .Site.Params }}
  {{ $index }} : {{ $element }}
{{ end }}
</pre>

{{< /my_shortcode.inline >}}

found on Hugo's discourse page

Upvotes: 1

talves
talves

Reputation: 14353

It will not be possible to access a shortcode from within your partials the way you are trying to set it up.

Alternative

Hugo allows you to call partials from your shortcodes as you have mentioned. Although the setup is not exactly the way you are trying to lay them out, you could create reusable partial components that would take $.Params and allow for close to what you are trying to accomplish.

  • Allow a shortcode to call any shortcode accessible partial component for reusability
  • Each partial component to stand as a stand alone reuseable shortcode or template from within your layouts

Create a place for your partial components. In this example use layouts/partials/component

Make a youtube video template like you would in a shortcode but use the passed $.Params as the context for the component. We will see this in the example below.

layouts/partials/component/youtube.html

<div>
  <iframe width="560" height="315" src="https://www.youtube.com/embed/{{ .id }}" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
</div>

Create the shortcode layouts/shortcodes/youtube-sc.html

{{ if .IsNamedParams }}
{{- partial "component/youtube" $.Params -}}
{{- end -}}

Call the shortcode from front matter markdown content/example.md:

---
title: Youtube example
---

{{< youtube-sc id="2xkNJL4gJ9E" >}}

Reuse the layouts/partials/youtube.html component in a list shortcode:

layouts/shortcodes/youtube-list.html

{{ if .IsNamedParams }}
  {{ $data := index $.Site.Data.youtube $.Params.data }}
  {{ range $data }}
    <h2>Video is {{ .id }}</h2>
    {{ partial "component/youtube" . }}
  {{ end }}
{{- end -}}

Call the shortcode from front matter markdown content/example.md:

---
title: Youtube List Example
---

{{< youtube-list data="videos" >}}

The data could include multiple lists and called based on the list of videos you want with {{< youtube-list data="others" >}} using:

---
videos:
- id: 2xkNJL4gJ9E
- id: FyPgSuwIMWQ
others:
- id: ut1xtRZ1QOA
- id: sB0HLHjgQ7E

NOTE: This example assumes the use of NamedParams in your shortcodes to make it cleaner. You could add data to the data objects also and pass that into the shortcode's named params as well.

Upvotes: 3

Related Questions