#DailyBloggingChallenge (322/365)
The nice thing about #hugo is how quickly one can get a site up and running.
The tricky part of it is understanding its directory architecture. This can become more difficult if using a user predefined theme, especially if one wants to one’s custom spin on nonexistent parameters.
#DailyBloggingChallenge (323/365)
One will learn that one will need to copy (usually) the whole file from the themes directory and mirror its directory structure onto the root level.
This can be quite a nuisance if one wants to change just one #CSS value.
#DailyBloggingChallenge (324/365)
Learning about the directory architecture opened the gates of writing custom partials.
I previous knew of their existence depending on the specific theme and usually could avoid them by injecting custom #HTML.
Now that I have been hired to work on a professional site, especially after propagating why to use #hugo over #Wordpress, I shall use the framework to its standards and fullest capabilities.
#DailyBloggingChallenge (327/365)
To inject custom #HTML in #hugo, one will either need to activate it in the config
[markup.goldmark.renderer]unsafe = true
or pipe the string with safeHTML
.
#DailyBloggingChallenge (328/365)
The initial partial that is created is a button for #paypal.
This is done by saving a paypal
param in the meta data of the article and then injected as an anchor tag onto the button.
The styling of the button is saved in a custom header partial that the theme provides.
#DailyBloggingChallenge (329/365)
This approach sounds quite good on paper, but #paypal carts don’t work so simplistically.
Although this might sound like a headache initially, in the long run it is quite beneficial.
The paypal cart or form has a couple values which are important.
The main one is the form id. This exists in either case of purchasing an item from a single button or from a dropdown.
In the case of a dropdown selection, then the keys and values of the table cells are important.
#DailyBloggingChallenge (330/365)
Now that these factors are known, one gets start building the params’ structure. The easiest is to have a param for the form id and an array of items for the store catalogue.
Each item of the array has a key, a value, and a image.
Further one can setup special params for the other table variables, though from brief comparison over three separate forms, they seem to stay constant.
#DailyBloggingChallenge (338/365)
The #HTML code from #PayPal looks like the image with these variables:
<PAYPAL_TOKEN>
is the main PayPal hook, so it knows how to update the store relative to all further actions taken within the form<PAYPAL_INIT_REFERENCE>
usually as on0
<PAYPAL_INIT_VALUE>
is usually equivalent to <PAYPAL_ITEM_1_VALUE>
<PAYPAL_INIT_NAME>
is usually equivalent to <PAYPAL_ITEM_1_NAME>
<PAYPAL_ITEM_1_NAME>
is usually equal to <PAYPAL_ITEM_1_VALUE>
<PAYPAL_ITEM_2_NAME>
is usually equal to <PAYPAL_ITEM_2_VALUE>
#DailyBloggingChallenge (339/365)
Through the abstraction of these variables and using the power of an array in the meta of #Hugo as #toml, one can create an item like in the image.
The array in this example is called paypalItems
and each element has the properties: name, value, image
. Further properties of the meta are paypal
as the <PAYPAL_TOKEN>
, price
should align with the price within the #paypal cart, paypalHeadName
as <PAYPAL_INIT_REFERENCE>
paypalHeadValue as <PAYPAL_INIT_VALUE>
.
Of course one could optimize the code somewhat by removing the variables that have INIT_(VALUE|NAME)
within them and replacing them by the first item 1_(VALUE|NAME)
. And replacing <PAYPAL_INIT_REFERENCE>
with on0
.
#DailyBloggingChallenge (340/365)
Now we will create a partial called paypal-button.html
within the layouts/partials/
directory. We want to call this partial like
{{ partial "paypal-button" (dict "paypal" .Params.paypal "items" .Params.paypalItems "headName" .Params.paypalHeadName "headValue" .Params.paypalHeadValue) }}
within a list template (either the default or another partial).
#DailyBloggingChallenge (341/365)
The question now is how to build the partial. Since #Hugo is a static site generator that focuses on using static elements like raw #HTML and #CSS, one quickly realizes the options to continue further are not many.
One could try to use the CSS option:checked
property and other pseudo classes within the <option>
HTML tag to turn on and off the various choices within the array. Though this becomes quite difficult to reference items outside of the <option>
tag and can make the whole thing an UX nightmare.
#DailyBloggingChallenge (343/365)
Time to make an overview of what we have so far:
(image Initial)
Since we are already using the callback approach, it would be better to move out everything that is not viable to the #paypal form. Thus making the overview more like
(image Reformed)
#DailyBloggingChallenge (345/365)
The next step is to create an unique value. Luckily #Hugo has a built in command via
{{ $uniqueID := .File.UniqueID }}
which needs to be placed within a range tag meaning within
{{ range .Pages }}
So the whole list.html
file would look like the image. We are also passing in the "uniqueID" $uniqueID
as the last parameter in the dict
for the partial "paypal-button"
.
#DailyBloggingChallenge (348/365)
In 342 the first mention of using a callback function is used without any concrete description of how it shall be built. From the last image one needs to change the partial reference to be saved as a variable which is then piped with safeHTML
, so
{{ $itemsOutput := partial "paypal-button" (dict "paypal" .Params.paypal "items" .Params.paypalItems "headName" .Params.paypalHeadName "headValue" .Params.paypalHeadValue "uniqueID" $uniqueID) }}
and the rendering is done via
{{ $itemsOutput | safeHTML }}
#DailyBloggingChallenge (349/365)
Further within the partial, since safeHTML
will be used one can parse string using #HTML and #JavaScript syntax within #GoLang. As an example
{{ $output := "" }}
{{ $output = print $output "<span>Example</span>" }}
{{ return $output }}
This creates a <span>
-tag with the text Example
after safeHTML
pipe is applied.
#DailyBloggingChallenge (350/365)
Now wrap the #HTML code (338) with a <div>
and a #CSS class that extends the uniqueID
(345) in its name. The pseudo code looks like the image.
#DailyBloggingChallenge (351/365)
Now that the #PseudoCode exists, it shall be converted into #Hugo syntax as described in 349. As in the image, some #GoLang functions are used like printf
and other Hugo functions to loop over the variables that are saved as an array type.
#DailyBloggingChallenge (352/365)
This code will generate dynamically based off of the length of the paypalItem
or .items
variable and use the first element of it as the default value (line 9).
Next one will extend the code with more CSS classes to ease the referencing of the JavaScript actions.
#DailyBloggingChallenge (353/365)
Looking at the #JavaScript code in the image that will be parsed over #GoLang using the printf
function.
First one creates a change
event listener function that is bound to the id of item-select-$uniqueID
. The <select>
-tag shall be added the id
attribute in line 13 (351).
Additionally a custom attribute data-image
shall be created and placed on the <option>
-tag in line 15 (351). This holds the path to the image value within the paypalItems
element.
Outside of the partial the #CSS classes selected-value
and selected-image
need to be defined. They present the respective values from paypalItems
element.
In the initial case, lines 10-11 will be called that take the value from the first element in the paypalItems
array. Defined from {{ $selectedValue := index .items 0 }}
which shall be prepended before the JavaScript code.
#DailyBloggingChallenge (354/365)
The whole code of the partial looks like in the image.
A lot of this code can be squashed to save space for the cost of readability.
#DailyBloggingChallenge (355/365)
Now that the partial is complete, look back at its parent. Combining the code from 345 and 348 one gets this image.
The problem of this code so far is that it doesn’t check if the $itemsOutput
exists. Further there are no checks that the array .Params.paypalItems
has any items and there is no default case.
#DailyBloggingChallenge (356/365)
Now one shall add the missing checks and default cases. Further one shall add the CSS classes mentioned in 353.
The result is seen in the image.
#DailyBloggingChallenge (357/365)
This concludes the explanation behind the process of creating a #Paypal partial for #Hugo using #GoLang tricks combined with #HTML, #CSS, and #JavaScript
The #TLDR can be read at https://bf5.eu/post/guide/hugo-how-to-create-paypal-partial/
#DailyBloggingChallenge (344/365)
Let’s say we would implement this pseudo code for the subdirectory
content/post/store
. It would work fine for one product in the subdirectorystore
.Now let’s add a second product that also has the same form (339) with different values.
If one would change either drop-down, both would update. The reason is that the #JavaScript callback is only checking for the #CSS class change and not for an unique class.
References