Search code examples
gohugojamstack

Conditionally render more than one section in Hugo


I want to render all the markdown files inside every folder except the static one in my home page of the website, one way of doing that is by using union in hugo, but as number of folders increase, I see myself repeating unions all over the place (code with union is commented, which is working by the way), so I thought using a slice would be a better idea, but when I try to use slice, I get the following error -

failed to render pages: render of "home" failed: "(Directory Path)\layouts\index.html:12:19": execute of template failed at <.Pages>: can’t evaluate field Pages in type string

directory structure

enter image description here

code for index.html

{{ define "main" }}
<ul class="homepage-topic-sections-container">
    {{$sectionNames := slice "posts" "problems" "tutorials"}}
    {{range $index, $sectionName := $sectionNames}}
    {{ range where .Pages "Section" $sectionName }}
    {{/*
    {{ range union (union
    (where .Pages "Section" "posts")
    (where .Pages "Section" "problems"))
    (where .Pages "Section" "tutorials")
    }}
    */}}
    <li>
        <section class="homepage-topic-section">
            <h1 class="topic-heading"><a href="{{.Permalink}}">{{.Title}} </a></h1>
            <div>
                {{ range .Pages }}
                <h3><a href="{{.Permalink}}">{{.Title}} &middot; {{.Date.Format "January 2, 2006"}}</a></h3>
                {{ end }}
            </div>
        </section>
    </li>
    {{end}}
    {{end}}

</ul>
{{ end }}

Solution

  • https://gohugo.io/templates/introduction/#the-dot:

    Context (aka “the dot”)

    The most easily overlooked concept to understand about Go Templates is that {{ . }} always refers to the current context.

    • In the top level of your template, this will be the data set made available to it.
    • Inside an iteration, however, it will have the value of the current item in the loop; i.e., {{ . }} will no longer refer to the data available to the entire page.

    In the code below, the dot in .Pages has the value of the current item in the first range action. The type of that value is string, and it does not have the field Pages. That's why it failed with execute of template failed at <.Pages>: can’t evaluate field Pages in type string.

    {{ define "main" }}
    <ul class="homepage-topic-sections-container">
        {{$sectionNames := slice "posts" "problems" "tutorials"}}
        {{range $index, $sectionName := $sectionNames}}
        {{ range where .Pages "Section" $sectionName }}
                       ^^^^^^
    

    One possible fix is to use $. to access the global context: .Pages ==> $.Pages.

    Maybe a better solution is to list the exclude sections instead. Then you don't need to modify the code when more folders are added:

    {{ define "main" }}
    <ul class="homepage-topic-sections-container">
        {{ range where .Pages "Section" "!=" "static" }}
        <li>