Skip to content

Commit

Permalink
Add examples to readme
Browse files Browse the repository at this point in the history
  • Loading branch information
crhntr authored Oct 29, 2024
1 parent ac188cb commit f139da9
Showing 1 changed file with 3 additions and 240 deletions.
243 changes: 3 additions & 240 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,245 +3,8 @@
Sometimes as a developer it is nice to stay in an HTML headspace.
This tool helps you do that and generates helpful test seams.

Given [main.go](#main_go) and [index.gohtml](#index_gohtml), muxt will generate [template_routes.go](#template_routes_go).
## Examples

### <span id='index_gohtml'></span> index.gohtml
```html
<!DOCTYPE html>
<html lang="en">
{{block "head" "example"}}
<head>
<meta charset='UTF-8'/>
<title>{{.}}</title>
<script src='https://unpkg.com/htmx.org@2.0.3/dist/htmx.min.js' integrity='sha384-0895/pl2MU10Hqc6jd4RvrthNlDiE9U1tWmX7WRESftEDRosgxNsQG/Ze9YMRzHq' crossorigin='anonymous'></script>
<script src='https://unpkg.com/htmx-ext-response-targets@2.0.0/response-targets.js'></script>
The directory ./example in this repository has a worked example.

<link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css'>
</head>
{{end}}
<body hx-ext='response-targets'>
<main class='container'>
<table>
<thead>
<tr>
<th>Fruit</th>
<th>Count</th>
</tr>
</thead>
<tbody hx-target="closest tr" hx-swap="outerHTML">

{{- define "view-row" -}}
<tr>
<td>{{ .Name }}</td>
<td id="count" hx-get='/fruits/{{.ID}}/edit'>{{ .Value }}</td>
</tr>
{{- end -}}

{{range .}}
{{template "view-row" .}}
{{end}}

{{define "edit-row" -}}
<tr>
<td>{{ .Row.Name }}</td>
<td>
<form hx-patch='/fruits/{{.Row.ID}}'>
{{block "count" . -}}
<input aria-label='Count' type='number' name='count' value='{{ .Row.Value }}' step='1' min='0'>
{{- end}}
<input type='submit' value='Update'>

<pre>{{.}}</pre>
</form>
<p id='error'>{{.Error}}</p>
</td>
</tr>
{{- end}}

{{- define "GET /{$} List(ctx)" -}}
{{template "index.gohtml" .}}
{{- end -}}

{{- define "GET /fruits/{id}/edit GetFormEditRow(id)" -}}
{{template "edit-row" .}}
{{- end -}}

{{- define "PATCH /fruits/{id} SubmitFormEditRow(id, form)" }}
{{- if .Error -}}
{{template "edit-row" .}}
{{- else -}}
{{template "view-row" .Row}}
{{- end -}}
{{ end -}}

</tbody>
</table>
</main>
</body>
</html>

{{- define "GET /help" -}}
<!DOCTYPE html>
<html lang='us-en'>
{{template "head" "Help"}}
<body>
<main class='container'>
Hello, help!
</main>
</body>
</html>
{{- end -}}
```

### <span id='main_go'></span> main.go

In your Go code focus on your domain. Let muxt generate handlers and wire them up with the HTTP mux (multiplexer).

```go
package main

import (
"context"
"embed"
"fmt"
"html/template"
"log"
"net/http"
)

//go:embed *.gohtml
var templateSource embed.FS

var templates = template.Must(template.ParseFS(templateSource, "*"))

type Backend struct {
data []Row
}

type EditRowPage struct {
Row Row
Error error
}

type EditRow struct {
Value int `name:"count" template:"count"`
}

func (b *Backend) SubmitFormEditRow(fruitID int, form EditRow) EditRowPage {
if fruitID < 0 || fruitID >= len(b.data) {
return EditRowPage{Error: fmt.Errorf("fruit not found")}
}
row := b.data[fruitID]
row.Value = form.Value
b.data[fruitID] = row
return EditRowPage{Error: nil, Row: row}
}

func (b *Backend) GetFormEditRow(fruitID int) EditRowPage {
if fruitID < 0 || fruitID >= len(b.data) {
return EditRowPage{Error: fmt.Errorf("fruit not found")}
}
return EditRowPage{Error: nil, Row: b.data[fruitID]}
}

type Row struct {
ID int
Name string
Value int
}

func (b *Backend) List(_ context.Context) []Row { return b.data }

//go:generate go run ../cmd/muxt generate --receiver-static-type Backend

func main() {
backend := &Backend{
data: []Row{
{ID: 0, Name: "Peach", Value: 10},
{ID: 1, Name: "Plum", Value: 20},
{ID: 2, Name: "Pineapple", Value: 2},
},
}
mux := http.NewServeMux()
routes(mux, backend)
log.Fatal(http.ListenAndServe(":8080", mux))
}
```

### <span id='template_routes_go'></span> template_routes.go
This file is generated by running `go generate` in the same directory as `main.go`
```go
// Code generated by muxt. DO NOT EDIT.

package main

import (
"context"
"net/http"
"strconv"
"bytes"
)

type RoutesReceiver interface {
SubmitFormEditRow(fruitID int, form EditRow) EditRowPage
GetFormEditRow(fruitID int) EditRowPage
List(_ context.Context) []Row
}

func routes(mux *http.ServeMux, receiver RoutesReceiver) {
mux.HandleFunc("PATCH /fruits/{id}", func(response http.ResponseWriter, request *http.Request) {
idParsed, err := strconv.Atoi(request.PathValue("id"))
if err != nil {
http.Error(response, err.Error(), http.StatusBadRequest)
return
}
id := idParsed
request.ParseForm()
var form EditRow
{
value, err := strconv.Atoi(request.FormValue("count"))
if err != nil {
http.Error(response, err.Error(), http.StatusBadRequest)
return
}
if value < 0 {
http.Error(response, "count must not be less than 0", http.StatusBadRequest)
return
}
form.Value = value
}
data := receiver.SubmitFormEditRow(id, form)
execute(response, request, true, "PATCH /fruits/{id} SubmitFormEditRow(id, form)", http.StatusOK, data)
})
mux.HandleFunc("GET /fruits/{id}/edit", func(response http.ResponseWriter, request *http.Request) {
idParsed, err := strconv.Atoi(request.PathValue("id"))
if err != nil {
http.Error(response, err.Error(), http.StatusBadRequest)
return
}
id := idParsed
data := receiver.GetFormEditRow(id)
execute(response, request, true, "GET /fruits/{id}/edit GetFormEditRow(id)", http.StatusOK, data)
})
mux.HandleFunc("GET /help", func(response http.ResponseWriter, request *http.Request) {
execute(response, request, true, "GET /help", http.StatusOK, request)
})
mux.HandleFunc("GET /{$}", func(response http.ResponseWriter, request *http.Request) {
ctx := request.Context()
data := receiver.List(ctx)
execute(response, request, true, "GET /{$} List(ctx)", http.StatusOK, data)
})
}
func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {
buf := bytes.NewBuffer(nil)
if err := templates.ExecuteTemplate(buf, name, data); err != nil {
http.Error(response, err.Error(), http.StatusInternalServerError)
return
}
if writeHeader {
response.Header().Set("content-type", "text/html; charset=utf-8")
response.WriteHeader(code)
}
_, _ = buf.WriteTo(response)
}
```
For a more complete example, see: https://github.com/crhntr/muxt-template-module-htmx

0 comments on commit f139da9

Please sign in to comment.