Retain 'html/template' examples and update README

This commit is contained in:
Adriano Caloiaro 2023-07-04 11:29:25 -06:00
parent e918ba6850
commit 5288d61023
No known key found for this signature in database
GPG key ID: C2BC56DE73CE3F75
7 changed files with 77 additions and 166 deletions

View file

@ -5,16 +5,16 @@ tmp_dir = "tmp"
[build]
args_bin = []
bin = "./tmp/hugo-htmx-go-template"
cmd = "go build -o ./tmp/hugo-htmx-go-template server.go"
cmd = "templ generate 2> /dev/null && go build -o ./tmp/hugo-htmx-go-template server.go"
delay = 1000
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
exclude_file = []
exclude_regex = ["_test.go"]
exclude_regex = ["_test.go", "_templ.go"]
exclude_unchanged = false
follow_symlink = false
full_bin = ""
include_dir = []
include_ext = ["go", "tpl", "tmpl", "html"]
include_ext = ["go", "tpl", "tmpl", "html", "templ"]
include_file = []
# 50ms in nanoseconds
kill_delay = 500000000

View file

@ -1,16 +1,25 @@
# hugo-htmx-go-template
This is a project template combining [Hugo](https://gohugo.io), [htmx](https://htmx.org), and an optional API server written in Go, using `html/template` for HTML rendering.
This is a project template combining [Hugo](https://gohugo.io), [htmx](https://htmx.org), and an optional API server written in Go, using `html/template` or [templ](https://github.com/a-h/templ/) for HTML rendering.
## Why?
Hugo is a fantastic static site build tool, and there are few things about Hugo that can or should be improved.
Hugo is a fantastic static site building tool, and there are few things about Hugo that can or should be improved.
The existence of this project template should in no way suggest that static sites should be dynamic. If your site _can_ be static, it _should_ be static.
The existence of this project template does not suggest that all static sites should be dynamic. If your site _can_ be static, it _should_ be static.
Yet there are instances in which one might need to add dynamic functionality to static Hugo sites. That is the purpose of this project template. Not to make all static sites dynamic, but to provide a simple solution to add islands of dynamic behavior to static sites.
Yet there are instances in which one might want to add dynamic functionality to static Hugo sites. That is the purpose of this project template. Not to make all static sites dynamic, but to provide a simple solution to add islands of dynamic behavior to static sites.
This allows us to build fast, easily deployable HTML content, but with the added ability of meeting a new class dynamic behavior needs.
This allows us to build fast, easily deployable HTML content, with the added ability of meeting a new class dynamic behavior needs.
Example use cases include
- Contact forms
- Comment systems
- Up/Down vote systems
- You know ... website stuff
You shouldn't have to reach for a SaaS product to offer dynamic content on your static sites.
## About
@ -24,8 +33,8 @@ This template provides example code and simple developer tooling for running Hug
**What developer tooling is provided?**
1. `bin/develop`: This utility both starts the hugo server (`hugo server`) and the API server (`go run server.go`) for development. If the user has `air` installed (https://github.com/cosmtrek/air), it will hot reload the API server code when changes to server.go are made.
2. `bin/build`: This utility builds a binary with your entire Hugo site embedded within. Note: this is only to be used to deploy Hugo sites as a self-contained binary.
1. `bin/develop`: This utility both starts the hugo server (`hugo server`) and the API server (`go run server.go`) for development. If the user has `air` installed (https://github.com/cosmtrek/air), API server code will hot-reload when changes to server.go are made.
2. `bin/build`: This utility builds a binary with your entire Hugo site embedded within. This allows Hugo sites to be deployed as a self-contained binary.
## Getting Started
@ -44,27 +53,37 @@ go build -o bin/build internal/cmd/build/main.go
mkdir public && touch public/.empty
```
If you'll be using the API server, it's useful to install `air` if you want auto-rebuild functionality when running `bin/develop`:
If you'll be using the API server, it's useful to install `air` to hot redeploy Go code changes when running `bin/develop`:
```bash
go install github.com/cosmtrek/air@latest
```
To make changes to `templ` templates (.templ files), you will need to install `templ`. Using `templ` is optional. The [goodbye world](https://github.com/acaloiaro/hugo-htmx-go-template/blob/main/server.go#L83) example shows `templ` in action.
```bash
go install github.com/a-h/templ/cmd/templ@latest
```
## Run
To start the development server(s), run `bin/develop`
Hugo will run on its develop port at [http://localhost:1313](http://localhost:1313) and the API server runs on [http://localhost:1314](http://localhost:1314).
Example content using Hugo/htmx/go api is available at: [http://localhost:1313/posts/hello-world/](http://localhost:1313/posts/hello-world/)
Example content is available at: [http://localhost:1313/posts/hello-world/](http://localhost:1313/posts/hello-world/)
You can also use `bin/build` to build a fat binary of your Hugo site, which will be available at [http://localhost:1314](http://localhost:1314) after running `build/server`.
`bin/build` can be used to build fat binaries of your Hugo site, which will be available at [http://localhost:1314](http://localhost:1314) after running `build/server`.
![screenshot](https://user-images.githubusercontent.com/3331648/248586236-1ad03704-4f13-418c-aa9a-3122742c6b8c.png)
## Changing `templ` templates
To use `templ` templates, you will need to `templ` installed. The `air` configuration watches for changes to `templ` files and automatically builds them.
## Deploy
Hugo sites made from this template can be deployed in two ways.
Hugo sites made from this template project can be deployed in two ways.
### Traditional deployment

View file

@ -4,9 +4,9 @@ date: 2023-06-23T15:58:00-06:00
draft: false
---
## Goodbye world is a static template example
## Goodbye world is a `templ` template example
Every time `Click Me!` is clicked, a request is sent to fetch static template `/goodbyeworld.html`.
Every time `Click Me!` is clicked, a request is sent to the API server, which renders `templ GoodbyeWorld()` from `partial/templates.templ`.
{{< html.inline >}}
<button

10
partials/helloworld.html Normal file
View file

@ -0,0 +1,10 @@
<div>
<p>This content is coming from our API server. It's a standard <strong>text/html</strong> template:
<strong>partials/helloworld.html</strong>. The template has a single variable named
<strong>Name</strong>, which it greets below.</p>
</div>
<p>If you set the <strong>name</strong> URL param, it greets you by name, e.g.
<a href="/posts/hello-world/?name=foobar">/posts/hello-world/?name=foobar</a></p>
<h3>Greeting: Hello, {{ .Name }}!</h3>

View file

@ -1,13 +1,5 @@
package partials
templ HelloWorld(name string) {
<div>
<p>This content is coming from our API server. It's a standard <strong>text/html</strong>template: <strong>partials/helloworld.html</strong>. The template has a single variable named <strong>Name</strong>, which it greets below.</p>
</div>
<p>If you set the <strong>name</strong>URL param, it greets you by name, e.g. <a href="/posts/hello-world/?name=foobar">/posts/hello-world/?name=foobar</a></p>
@HelloWorldGreeting(name)
}
templ HelloWorldGreeting(name string) {
<h3>Greeting: Hello, { name }!</h3>
}

View file

@ -1,4 +1,4 @@
// Code generated by templ@(devel) DO NOT EDIT.
// Code generated by templ@v0.2.304 DO NOT EDIT.
package partials
@ -9,7 +9,7 @@ import "context"
import "io"
import "bytes"
func HelloWorld(name string) templ.Component {
func HelloWorldGreeting(name string) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
templBuffer, templIsBuffer := w.(*bytes.Buffer)
if !templIsBuffer {
@ -22,149 +22,26 @@ func HelloWorld(name string) templ.Component {
var_1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, err = templBuffer.WriteString("<div><p>")
if err != nil {
return err
}
var_2 := `This content is coming from our API server. It's a standard `
_, err = templBuffer.WriteString(var_2)
if err != nil {
return err
}
_, err = templBuffer.WriteString("<strong>")
if err != nil {
return err
}
var_3 := `text/html`
_, err = templBuffer.WriteString(var_3)
if err != nil {
return err
}
_, err = templBuffer.WriteString("</strong>")
if err != nil {
return err
}
var_4 := `template: `
_, err = templBuffer.WriteString(var_4)
if err != nil {
return err
}
_, err = templBuffer.WriteString("<strong>")
if err != nil {
return err
}
var_5 := `partials/helloworld.html`
_, err = templBuffer.WriteString(var_5)
if err != nil {
return err
}
_, err = templBuffer.WriteString("</strong>")
if err != nil {
return err
}
var_6 := `. The template has a single variable named `
_, err = templBuffer.WriteString(var_6)
if err != nil {
return err
}
_, err = templBuffer.WriteString("<strong>")
if err != nil {
return err
}
var_7 := `Name`
_, err = templBuffer.WriteString(var_7)
if err != nil {
return err
}
_, err = templBuffer.WriteString("</strong>")
if err != nil {
return err
}
var_8 := `, which it greets below.`
_, err = templBuffer.WriteString(var_8)
if err != nil {
return err
}
_, err = templBuffer.WriteString("</p></div><p>")
if err != nil {
return err
}
var_9 := `If you set the `
_, err = templBuffer.WriteString(var_9)
if err != nil {
return err
}
_, err = templBuffer.WriteString("<strong>")
if err != nil {
return err
}
var_10 := `name`
_, err = templBuffer.WriteString(var_10)
if err != nil {
return err
}
_, err = templBuffer.WriteString("</strong>")
if err != nil {
return err
}
var_11 := `URL param, it greets you by name, e.g. `
_, err = templBuffer.WriteString(var_11)
if err != nil {
return err
}
_, err = templBuffer.WriteString("<a href=\"/posts/hello-world/?name=foobar\">")
if err != nil {
return err
}
var_12 := `/posts/hello-world/?name=foobar`
_, err = templBuffer.WriteString(var_12)
if err != nil {
return err
}
_, err = templBuffer.WriteString("</a></p>")
if err != nil {
return err
}
err = HelloWorldGreeting(name).Render(ctx, templBuffer)
if err != nil {
return err
}
if !templIsBuffer {
_, err = io.Copy(w, templBuffer)
}
return err
})
}
func HelloWorldGreeting(name string) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
templBuffer, templIsBuffer := w.(*bytes.Buffer)
if !templIsBuffer {
templBuffer = templ.GetBuffer()
defer templ.ReleaseBuffer(templBuffer)
}
ctx = templ.InitializeContext(ctx)
var_13 := templ.GetChildren(ctx)
if var_13 == nil {
var_13 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
// Element (standard)
_, err = templBuffer.WriteString("<h3>")
if err != nil {
return err
}
var_14 := `Greeting: Hello, `
_, err = templBuffer.WriteString(var_14)
// Text
var_2 := `Greeting: Hello, `
_, err = templBuffer.WriteString(var_2)
if err != nil {
return err
}
var var_15 string = name
_, err = templBuffer.WriteString(templ.EscapeString(var_15))
// StringExpression
var var_3 string = name
_, err = templBuffer.WriteString(templ.EscapeString(var_3))
if err != nil {
return err
}
var_16 := `!`
_, err = templBuffer.WriteString(var_16)
// Text
var_4 := `!`
_, err = templBuffer.WriteString(var_4)
if err != nil {
return err
}
@ -187,17 +64,19 @@ func GoodbyeWorld() templ.Component {
defer templ.ReleaseBuffer(templBuffer)
}
ctx = templ.InitializeContext(ctx)
var_17 := templ.GetChildren(ctx)
if var_17 == nil {
var_17 = templ.NopComponent
var_5 := templ.GetChildren(ctx)
if var_5 == nil {
var_5 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
// Element (standard)
_, err = templBuffer.WriteString("<p>")
if err != nil {
return err
}
var_18 := `Goodbye, World!`
_, err = templBuffer.WriteString(var_18)
// Text
var_6 := `Goodbye, World!`
_, err = templBuffer.WriteString(var_6)
if err != nil {
return err
}

View file

@ -1,11 +1,13 @@
package main
import (
"bytes"
"embed"
"fmt"
"io/fs"
"log"
"net/http"
"text/template"
"github.com/a-h/templ"
"github.com/acaloiaro/hugo-htmx-go-template/partials"
@ -25,6 +27,9 @@ func main() {
return func(w http.ResponseWriter, r *http.Request) {
// in development, the Origin is the the Hugo server, i.e. http://localhost:1313
// but in production, it is the domain name where one's site is deployed
//
// CHANGE THIS: You likely do not want to allow any origin (*) in production. The value should be the base URL of
// where your static content is served
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, hx-target, hx-current-url, hx-request")
@ -58,10 +63,16 @@ func helloWorld(w http.ResponseWriter, r *http.Request) {
name = "World"
}
if err := partials.HelloWorld(name).Render(r.Context(), w); err != nil {
w.WriteHeader(http.StatusInternalServerError)
tmpl := template.Must(template.ParseFiles("partials/helloworld.html"))
var buff = bytes.NewBufferString("")
err := tmpl.Execute(buff, map[string]string{"Name": name})
if err != nil {
ise(err, w)
return
}
w.WriteHeader(http.StatusOK)
w.Write(buff.Bytes())
}
// this handler accepts POST requests to /hello_world_form