Dynamically set HTML form action URL?

In most HTML forms in Hugo templates that I’ve seen, the form action URL is statically set like the following.

<form method="POST" action="{{ .Site.Parmas.foo }}">
...
</form>

I’m thinking of setting the form action URL dynamically with JavaScript to stop spam bots after reading this article. In this way, the JavaScript needs to load parameters from the site config.

However, in the discussion from Templating JS?, in which @carlmjohnson wrote

You should not want to do this. JS files are code and combining code and templating leads to madness. Trust me from experience on this one, the results are instantly unreadable spaghetti.

I tried loading a JS file with Hugo template code with these lines

{{ $smjs := resources.Get "js/staticman.js" | resources.ExecuteAsTemplate "js/staticman.js" }}
<script src="{{ $smjs.RelPermalink }}"></script>

but I ran into error.

ERROR 2019/07/26 18:47:58 Rebuild failed:

ERROR 2019/07/26 18:47:58 Failed to render pages: render of "page" failed: "/home/vin100/huginn/layouts/_default/single.html:71:63": execute of template failed: template: _default/single.html:71:63: executing "main" at <resources.ExecuteAsTemplate>: error calling ExecuteAsTemplate: must provide targetPath, the template data context and a Resource object

Well, that’s one opinion. Personally, I don’t see the harm for small snippets.


As for your error, as the message reads, you need to supply a target path. I would rewrite it as:

{{ $path := "js/staticman.js" }}
{{ $smjs := resources.Get $path | resources.ExecuteAsTemplate $path $path }}
<script src="{{ $smjs.RelPermalink }}"></script>

1 Like

Thanks for answer. Before reading your answer, I’ve actually solved it with a dot . in the 2nd parameter.

{{ $smjs := resources.Get "js/staticman.js" | resources.ExecuteAsTemplate "js/staticman.js" .Site.Params.staticman }}
<script src="{{ $smjs.RelPermalink }}"></script>

P.S. I’m using Hugo template code in js/staticman.js to load the form action URL, so the Staticman parameters in the config file suffice.

For the record, executing a JS file as a template is an example of “combining code and templating” which “leads to madness”.

Here is something simple you can do instead if you think you need to obscure the URL of your action (not sure that’s necessary, but let’s assume it is):

In Hugo, you are going to use templates to add attributes:

<form data-secret-action="{{ site.Params.foo | base64Encode }}">

Then in JavaScript, you read in the attributes:

(function () {
  var form = document.querySelector("[data-secret-action]");
  if (!form) {
    return;
  }
  var url = btoa(form.dataset.secretAction);
  form.action = url;
})();

Following this pattern whenever you need to pass data from Hugo to JavaScript is the only road to sanity. Executing JS as a template, whether the JS on is on the page or in a separate file, will lead to endless suffering because your code has two meanings at once—the JS meaning and the Hugo meaning—and those two meanings are interwoven with no clear delineation.