[Solved] Nested "where" filters in a .Pages range statement - using .Scratch variable to offset the range index

for comparison here is the working template partial without the “offset” code

<!-- Renders Section Based on Content -->
{{ range $i, $e := .Data.Pages }}
{{ if ne .Params.hidden true }}
<section id="nav-{{ .File.BaseFileName }}" class="section section--{{ .File.BaseFileName }} section--{{ if modBool $i 2  }}odd{{ else  }}even{{ end }}">
    <div class="section__container">
        <div class="section__headline section__headline--{{ .File.BaseFileName }}">
            <h1>{{ .Title }}</h1>
        </div>
        <div class="section__content section-content--{{ .File.BaseFileName }}" {{ with .Params.align }} style="align-items: {{.}};"{{ end }} >
            {{ .Content }}
        </div>
    </div>
</section>
{{ end }}
{{ end }}

I don’t think you need scratch, but you can do this:

{{ $.Scratch.Add "i" 1 }}
  {{ $i := $.Scratch.Get "i" }}

{{ if modBool $i 2 }}...

I see looking over the right in your code (the version without scratch) you have {{ if modBool $i 2 }}

That’s not working?

I think I’m starting to see. Not sure, but I think you do have to use scratch, setting it at the beginning of your loop and incrementing it inside the conditional. Also, it looks like everything you’re returning is ne .Params.hidden true so you might just use a where statement when defining your range, and that would be more straightforward incrementing.

Maybe this (simplified) will work for you:

{{ range $i, $e := where (.Data.Pages) ".Params.hidden" "!=" true }}

{{ .Title }}{{ $i}}{{ if modBool $i 2 }} is even {{ end }} 

{{ end }}
1 Like

The second posted code works fine. Yes I think I need .Scratch because of the changes in closure with conditionals an loops.

Basically if a section/page to be rendered has a “hidden” parameter then I want to skip the even/odd incrementing of the loop index. Also there is a possibility the user will have a custom hero section/page and that doesn’t get counted either. Since I’m using mod that’s as easy as just adding 1 to an offset which will then “skip” the index so the sections get always the alternating even odd BEM class. If for example someone hides a section then without this two adjacent visibile sections will be either both even or both odd and thus be the same color given my css.

Thanks @budparr. If I include the conditional in the loop then I don’t have to worry about extraneous index values? I’ll try and see. Skipping the hero section could be added as well. And now I see how to easier increment a scratch variable.

@budparr mucho thx.

{{ range $i, $e := where (.Data.Pages) ".Params.hidden" "!=" true }}

Worked great. By using filtering the index was not incremented on pages filtered…perfect.

@budparr do you know the syntax to add a second “or” condition to the filter? I struggle with syntax in go templates with few examples in the docs.

something like this??

{{ range $i, $e := where (.Data.Pages) ( or  (".Params.hidden" "!=" true )  ( ".File.BaseName" "!=" "hero" ))  }}

or this

{{ range $i, $e := where (.Data.Pages)   (".Params.hidden" "!=" true ) |  ( ".File.BaseName" "!=" "hero" )  }}

I think it’s something like this:

{{ if or ( ".Params.hidden" "!=" true )  (".File.BaseName" "!=" "hero") }}

I was just recently writing something like that. I’ll see if I can find it.

1 Like

by the way ( “.Params.hidden” “!=” true ) = ( “.Params.hidden” false )

I’m not sure that’s true, @Mikhail, at least in this case, - because an empty value will also be false, and I don’t think that’s the intent of having a hidden key.

1 Like

@dkebler I think @budparr 's suggestion is the best one, but just a couple notes:

  1. You’d still have to range through the pages first and then set up your or as a second condition inside the loop.
  2. You can’t use or conditions within a where statement, but you can nest your where's as a sort of and conditional.
  3. I prefer Bud’s use of the mathematical symbols, but his example can be written in more than one way, depending on your taste:

{{ if or ( not .Params.hidden ) ( ne .File.BaseName "hero") }}

Slightly more terse, but I still like the above example better. Just as an FYI that not is a handy item to have in your arsenal and to Bud’s point, an empty string will evaluate to false and therefore not in this case.

Putting it all together, and I’m only about 90% certain that I know exactly what you’re trying to do:

{{ range $i, $e := .Data.Pages }}
    {{ if or ( ".Params.hidden" "!=" true )  (".File.BaseName" "!=" "hero") }}
      Codey code-a-roo....
    {{ end }}
{{ end }}

One gotcha for me—and it looks like you already figured this out—is that Go templates are pretty particular about spacing. I spent hours trying to figure out a template once before realizing it was just that I forgot to put a single space between my parens-grouped conditionals; e.g., {{ if or ( not .Params.hidden )( ne .File.BaseName "hero") }} won’t work because of n )( ne rather than n ) ( ne.

@rdwatters. Actually I was doing something like that before but the issue here is filtering after the fact (after the range function) means the index is incremented even though the page ends up being filtered. When this happens it screws up my modulo calculation which was alternating the colors of each rendered page (page as a visible section in this case) based on adding either a --even or --odd class.

If what you are saying is there is NO way to add a second condition within the “where” itself then I’d have to go back to my original first post that plays games with the index $i so that the modulo 2 works out when hero section is filtered.

Rats…now I have a third condition for where which is I need to filter on a “section”

So is it possible to construct a multiple condition within the range/where construct. And if so what is the syntax?

I offered a few lame attempts above.
.

@dkebler I’m a little lost in what you’re trying to accomplish (as opposed to what you’re trying to do in code). Could you maybe spell it out without code, please?

1 Like

@budparr This is what I am trying to do. where $i is the index of the range loop (see very first post of this thread)

 section--{{ if modBool $i 2  }}odd{{ else }}even{{ end }}">

now if a page is filtered after the where statement (for example it’s hidden) then the modulo calculation will be incorrect as $i has been incremented for a hidden page.

When you suggested putting the filter/condition in the where statement then that issue goes away but if I filter with an “if” condition after then $i is incremented then the modBool statement above is using the wrong value

So either

I must put all my page filters within the range/where statement (hidden, hero or not, and from a particular section). something like this?

{{ range $i, $e := where (.Data.Pages)   (".Params.hidden" "!=" true ) |  ( ".File.BaseName" "!=" "hero" ) | ( "Sections" "mysection"  }}

or

I filter afterward and if something gets filtered then increment some counter to the modulo so it works out ok. Like this

if condtions met {{ .Scratch.Add "count" 1 }}
then
section--{{ if modBool ( .Scratch.Get "count" ) 2 }}odd{{ else }}even{{ end }}">

Method 1 is way easier but only if multiple conditions can be placed in range/where statement. How about starting there. Is that possible. If so what is the syntax.

I mean what you are trying to accomplish. What is the outcome you are looking for? Without code, please.

get each page in a hugo content/section to render in alternating colors using even/odd BEM modifer class. This works great. I’ve built a single page website using md pages one for each section. Here is an example of where I have used my theme http://4005.kebler.net. Now I want to add the feature of hiding some pages with a page parameter and that messes up the alternating classes since I get those based on the range index.

@budparr what you proprosed in the other thread works. Nested where’s

{{ range where ( where .Site.RegularPages  "Section" "sections" ) ".Params.hidden" "!=" true }} 

I can take it from here. Thanks for you input and time. I’ll call this solved.

https://discuss.gohugo.io/t/filter-data-pages-based-on-content-subdirectory-without-using-a-page-parameter/6050/5

1 Like

glad to hear it!

For those who might come here later here is my final working solution.

My range has two nested where’s and then uses a scratch offset variable and the “add” function to offset the range index if a hero.md file exists in the section.

{{ $.Scratch.Set "offset" 0 }}
{{ range $i, $e := where ( where .Site.RegularPages  "Section" "sections" ) ".Params.hidden" "!=" true }}
{{ if eq .File.BaseFileName "hero" }}{{ $.Scratch.Set "offset" 1 }}{{ end }}
{{ $j := add ( $.Scratch.Get "offset" ) $i }}
<section id="{{ .File.BaseFileName }}" class="section section--{{ .File.BaseFileName }} {{ if ne .File.BaseFileName "hero" }}section--{{ if modBool $j 2  }}odd{{ else  }}even{{ end }}{{ end }}">
    <div class="section__container">
        <div class="section__headline section__headline--{{ .File.BaseFileName }}">
            <p>a test {{ $j }}</p>
            <h1>{{ .Title }}</h1>
        </div>
        <div class="section__content section-content--{{ .File.BaseFileName }}" {{ with .Params.align }} style="align-items: {{.}};"{{ end }} >
            {{ .Content }}
        </div>
    </div>
</section>
{{ end }}
2 Likes

Not sure if it helps but you can do this sort of thing a lot easier just with CSS - e.g. using :nth-child(even) and :nth-child(odd) selectors …

1 Like