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
Hugo expects the parameter to be written as “notonhomepage” in index.html template even though front matter defines it as “notOnHomePage”.
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")}}
{{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")}}
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.
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.
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.
@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.
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.