How to filter pages on homepage based on page params

I have a working layout for home page which looks like this:

{{range first 10 .Paginator.Pages}}

However, there are certain pages which I dont want to show up on home page. So I define a specific notOnHomePage flag and ideally, when set on any post, it should not be shown in the homepage.

In font matter:

+++
...
notOnHomePage = "true"
+++

In index.html

...
{{range first 10 (where .Paginator.Pages ".Params.notOnHomePage" "!=" "true")}}

But now nothing gets shown on the home page and there is no error either while compiling hugo

Any help will be greatly appreciated.

Couple of updates:

  1. Hugo expects the parameter to be written as “notonhomepage” in index.html template even though front matter defines it as “notOnHomePage”.

  2. This is working:

{{range first 10 (where .Paginator.Pages ".Params.notonhomepage"  "true")}}

i.e the only those pages which are not supposed to be shown are being rendered.
Point is the above logical comparision is working. But I dont know how to inverse it.

Maybe hugo is silently failing on the pages where “notOnHomePage” is not defined.

Can someone tell me how to inverse the logical comparison in above mentioned snippet.
I tried following but not working:

{{range first 10 (where .Paginator.Pages ".Params.notonhomepage" "!=" "true")}}

Thanks.

You can do this with not ( your condition ).

I didn’t checked if it works, but try this snippet:

{{range first 10 (where .Paginator.Pages not(".Params.notonhomepage" "!=" "true"))}}

Didn’t work.

In fact rest of the contents on home page also don’t get generated which suggests that hugo is silently eating errors which is bad.

@tatsushid knows the most about the where filter

@ashishthedev please file a GitHub issue about the “error eating”. Putting it in bold in a thread on a discussion forum doesnt’t solve it.

And it might help to read up on the documentation. In programming it often helps to do it exactly by the book:

I think

{{range first 10 (where .Paginator.Pages ".Params.notonhomepage" "!=" "true")}}

would work if you also set notOnHomePage = "false" in any other pages.

where function searches the second parameter’s value and when the base parameter (in this case, .Param) is map type, it returns zero Value if there isn’t such a key in the map. zero Value is treated as a special case in a condition check in where function. It isn’t evaluated in it and always returns false

In this case, I guess you only have notOnHomePage = "true" so

{{range first 10 (where .Paginator.Pages ".Params.notonhomepage" "!=" "true")}}

returns false if it meets such a page and also returns false if there isn’t the key’s value in map, means nothing is rendered.

If adding notOnHomePage = "false" to other pages annoys you, I suggest simply adding OnHomePage = "true" and using

{{range first 10 (where .Paginator.Pages ".Params.onhomepage" "true")}}
1 Like

It may be better to print “no such a key value” if where function can’t find a key’s value to help finding out what causes a problem.

The idea was that privacy and about pages are coming in my homepage.
So I though I would just add “notOnHomePage” on these specific pages and filter them out.
But as it turns out, the “where” functionality is broken when this page specific parameter is not present on normal pages.

Going the other way, i.e. having tag “OnHomePage = true” is not a good option as this really is the main workflow and not an exception. If I am going to write lets say 200 posts in coming future, it is better to put the tag on just 2 or 3 pages instead of on 200.

Hope that clarifies the use case.

Thanks.
Github issue links:


I don’t think where behavior is wrong. Comparing with “not exist” value is impossible and I don’t want to write the same story about NULL in the SQL world here.

But mentioned above, it’s highly inconvenient not to able to filter such values and yes, I agree with you I also don’t want to write a parameter for filtering time and time.

So, how about introducing nil (NULL value in Go) comparison to where? In this case, the point is whether notOnHomepage parameter exists or not. If there is nil comparison, we can write

where .Paginator.Pages ".Params.notonhomepage" nil

or

where .Paginator.Pages ".Params.notonhomepage" "==" nil

To invert the result, just write

where .Paginator.Pages ".Params.notonhomepage" "!=" nil

"param" "==" nil means the parameter doesn’t exist. "param" "!=" nil means it exists.

According to http://golang.org/pkg/text/template/, nil can be used in a template so it works. Actually, I’ve tested it could filter a particular article by the rules above and it worked good.

If this sounds reasonable, I’ll send PR

2 Likes
  • where behaves as I expect in this area. It is trivial to write a script that provides default values for historic content’s default values. New content would be handled by archetypes
  • It would, however, be cool to have ´nil´ support, if not too much work

After sitting on this for a couple of days, I think I have a better solution.

“where” should accept a predicate much like the “if” clause in python’s list comprehensions.

newList = [x for x in oldList if x != nil]

That predicate should return a bool for each element in the list. If true, that page/node should be retained in the resulting list.

If I understand correctly, “where” is the only construct which can alter/transform the default list(such as .Paginator.Pages) . And it is too limiting. You can only check for one condition. If where can support the predicate functionality discussed above, the whole problem could have been gracefully solved in the following manner.

{{range first 10 (where .Paginator.Pages ( and (isset ".Params" notonhomepage) ( index .Params "notonhomepage" "!=" "true"))}}

The benefit of the predicate is that as an end user, I can have all the logic and can chain all the operations to filter out non-required pages.

Not sure if this will break backward compatibility, but will be super useful. If required, hugo should support this functionality even if it means implementing a new keyword apart from "where "(may be call it “if” or “filter”)

@spf13 and @bep may wanna comment on this “predicate” thing.

First, where isnt’t the only way to transform the list – there is first and a few others.

Re. predicate,

The suggestion from @tatsushid is more in line with my world view.

@bep, I’ve already wrote the patch for introducing nil comparison yesterday (not big, modified about 10 lines) so it’s easily added just sending PR and merging it.

@ashishthedev, yes, where can only check one condition. When I implemented it, I referred Jekyll’s where template function and made it have a similar syntax.

In current where limitation, to apply multi conditions to an array like your predicate case, it have to be written like following (with nil comparison)

where (where .Data.Pages ".Params.notonhomepage" "!=" nil) ".Params.notonhomepage" "!=" "true"

where just returns filtered list so it can be reused. I confirmed it worked with my nil comparison patched hugo.

BTW, the idea supporting predicate is interesting. I haven’t checked whether it’s possible to evaluate go template string in where so it worth considering. http://golang.org/pkg/text/template/parse/ and it’s source code or hugo’s shortcode implementation may tell that.

The example above may displayed in two or more lines but I wrote it in a line. Of course, it can also be written

$filtered := where .Data.Pages ".Params.notonhomepage" "!=" nil
where $filtered ".Params.notonhomepage" "!=" "true"

Wow. This looks very promising. Let me try it and post results here.

Slightly related question: Can I use isset with where?

I sent PR and the my original branch is available at https://github.com/tatsushid/hugo/tree/feature/nil-comparison-in-where so now you can try it

Now where doesn’t understand isset but it’s just a syntax sugar for "!=" "nil" so it seems to be easily added

No, drop that. Aliases with no real value just adds complexity and documentation.