Helper functions acccessible by template

Is there any way to create a Go (or js) function that can be called by the template at processing stage?

I was trying to create an HTML list of pages by an order not covered in the template language (in other words not by weight, etc).

Plus working with Hugo templates and {{ range }} has made me see the logic to logic-less templates. That is, why not be able too do something that is common in mainstream languages because the one available is a limited templating language?

As I understand it, Handlebars works by being able to calll functions and variables, but not manipulate within the templatee. Although at first I thought this was adding uneccesary layers, I now see it also allows full acccess to a mainstream language (without limitations) acccesible by the template.

Any way to do this or something similar in Hugo? I’d need to learn Go, but i think the time spent there is better than the odd work-arounds I’m finding I’m doing coding within the templates.

1 Like

The template system that we use supports helpers and Hugo has already provided quite a few including range.

See https://github.com/spf13/hugo/blob/master/tpl/template_funcs.go

I’m looking at that. It might be an area of Hugo that I’m not taking enough advantage of.

The reason for wanting a custom helper is because I wanted to sort a range of .data.pages by url or relpermalink.

Am I right that with range as-is I’m not able to search by relpermalink? The sortbyfield seems to only accept value, and the other options I found were byweight, bytitle and some groupby options.

What is the order when no sort option is chosen? Normally that would be weight (the docs say Weight -> Date), but in ths case it seems not to be as ByWeight bives a very different order to no sort option.

I have not read your entire problem-area, but Hugo’s template functions is nothing like Handlebars. They are not custom, but compiled in. But, if you combine them, you should be able to do just about anything.

Summary is I wanted to sort .Data.Pages by relpermalink.

The only way I could see to do this was to itterate once through, store it as a new string with relpermalink as the top value, split the string into array and itterate again.

Doing it this way with template funtions rather than a Go helper function means 1) it’s long-winded 2) there’s too much code in my template.

(If you’'re wondering, my aim was to sort content in the order it exists in the content folders)

Perhaps I am missing something here.

With this:

{{ range where $.Site.Pages "Section" "post" }}

and assuming I have a structure as follows (nested folders and markdown files):

content
–post
----sport
-------post7.md
-------tennis
----------post1.md
----------post5.md
-------badminton
----------post2.md
-------other
----------post3.md
----------post4.md
----games
-------post5,md

–pages

…what order would I get in the range? Would the posts be order as they are in the content structure, would they be alphabetical based on filename, or something other? Or could thhere be a random element in this?

They should be ordered by (in following priority) weight, date (in front matter if set) and link title.

Thanks

With empty front matter (other than perhaps title set), what would determine weight? There’s no date or lin title in front matter.

If no Weight, Date or Title … Then you cannot count on any specific order for the default page sort.

Ok, thanks. That makes sense as I was sure I had had different results from earlier tests.

Do you know if it’s possible to programmticallly set weight for pages or any type of array?

The reason is I’m working on a test of a blog which uses the source content folder / sub-folder / sub-sub-folder / etc location as the categorisation (and tagging) rather than front matter. This may sound odd now, but I think the working example will clarify when ready.

From the Go-side that is trivial … but you would have to recompile Hugo. I kind of understand what you are looking for, and it would be possible to deduct some front matter values (title, date) from filename etc. But it does sound like a special use case.

Thinking about it, it should be possible to use the sort template func and sort by file name/path.

I was thinking this might be the answer. I will look into this annd see how I get on with Go.

I looked into thiis initially but I couldn’t find any example or reference that used sort for anything oother than ‘value’. I tried various other things in place of value but none worked.

If I could sorting by file path this would help a great deal.

I’ve worked out a way to catogorize and also create psuedo-tags using .RelPermalink. Just the sorting is the issue.

If you try the latest Hugo dev version, I believe you should be happier:

Blimey, that’s surprising (in a good way)! I was not expecting such a direct solution. The way I had attempted to do this (creating a new arrray from data.pages with relpermalink as the first index) was messy. This is much better.

I presume I need to build the dev version? I will look now at installing Go and this process (it’'s something I should learn anyway).

So, an update (that was an interesting look into Hugo code, Go and Git)

The new compiled master works well, but of course I’ve got Title set in the front matter, so it was sorting by that. Works great when I remove title.

Therefore, as an experiement, and to be able to use title in front matter and also sort by file path, I added a new function to the file you modified, on my local Hugo source (I’m not familiar with cloning, comitting, etc in github).

So I compiled this local version and this new function seems to work ok, thanks to your modified code as a guide (as I have no prior knowledge of Go).

New function I put in pageSort.go is:

// ByFilePath sorts the Pages by file path and returns a copy.
//
// Adjacent invocactions on the same receiver will return a cached result.
//
// This may safely be executed in parallel.
func (p Pages) ByFilePath() Pages {
key := “pageSort.ByFilePath”

FullFilePath := func(p1, p2 *Page) bool {
    return p1.FullFilePath() < p2.FullFilePath()
}
pages, _ := spc.get(key, p, PageBy(FullFilePath).Sort)
return pages

}

and I’'ve tested it like this:

{{ range where $.Site.Pages.ByFilePath "Section" "product" }}

and it seems to work.

If anyone else wants this or if I can / should include in github version, let me know.

1 Like

I know one project that would need this functionality.

File path sorting would be nice indeed :+1: . It can be thought as a weight defined with the baseFilename value.

I feell like this is an interesting one but the substance was removed from the initial question:

Helper functions accesible by template?”

I was wandering if possible to use a go library like “GitHub - lucasb-eyer/go-colorful: A library for playing with colors in go (golang).

to make something like below:

{{- $hexColor := "#FF5733" -}}
{{- $hslColor := convertHexToHSL $hexColor -}}
<div>
  <div style="background-color: {{ $hexColor }};">
    Hex: {{ $hexColor }}<br>
    HSL: {{ $hslColor }}
  </div>
</div>
1 Like

The ability to create functions would go a long way to cleaning up our template code. A function I’m used to writing in other web frameworks is my own improved titlecase() to handle acronyms and well known names like:

php        → PHP
typescript → TypeScript

My workaround for lack of that, in my current generated-content site, is to create data/ files with mappings like these. And then (repetitively) call them from templates.

I suspect that i18 files or partials could be abused to return a simple string result for these.

I also miss the ability to create custom predicates—functions that return a boolean value—which can easily be dropped into an {{ if }}. E.g.:

is_hebrew_or_arabic() → <boolean>

I can imagine that we don’t have these abilities because we’re making reuse of Go and Go templating. That’s great architecture in theory, but IMO for web scripting, it can become a burden.

How hard would it be to support a Handlebars or similar, say, via a --preprocessor option?