hrender-examples/datastar/main.go

177 lines
4.0 KiB
Go

package main
import (
"fmt"
"log"
"net/http"
"os"
"sort"
"strconv"
"github.com/starfederation/datastar-go/datastar"
"github.com/zepyrshut/hrender"
)
type TodoList struct {
ID int
Name string
Completed bool
}
var todoList = []TodoList{
{
ID: 1,
Name: "Sacar a la perra",
Completed: false,
},
{
ID: 2,
Name: "Limpiar la casa",
Completed: false,
},
{
ID: 3,
Name: "Ir al supermercado",
Completed: false,
},
}
type TodoSignals struct {
NewTodoName string `json:"newtodo"`
}
func main() {
templatesFS := os.DirFS("./templates")
h := hrender.NewHTMLRender(templatesFS, false)
mux := http.NewServeMux()
mux.Handle("GET /", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set(hrender.ContentType, hrender.ContentTextHTMLUTF8)
err := h.RenderW(w, "pages/index", hrender.H{})
if err != nil {
http.Error(w, "error loading template", http.StatusInternalServerError)
}
}))
mux.Handle("GET /todo", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
sort.Slice(todoList, func(i, j int) bool {
return todoList[i].ID > todoList[j].ID
})
templ, err := h.RenderS("fragments/todo-widget", hrender.H{
"TodoList": todoList,
})
if err != nil {
http.Error(w, "error loading template", http.StatusInternalServerError)
}
sse := datastar.NewSSE(w, r)
if err := sse.PatchElements(templ); err != nil {
log.Println("error", err)
}
}))
mux.Handle("GET /todo/new", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
templ, err := h.RenderS("fragments/todo-row-edit", hrender.H{})
if err != nil {
http.Error(w, "error loading template", http.StatusInternalServerError)
}
sse := datastar.NewSSE(w, r)
if err := sse.PatchElements(templ,
datastar.WithModePrepend(),
datastar.WithSelectorID("todo-list-body"),
); err != nil {
log.Println("error", err)
}
}))
mux.Handle("POST /todo", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var signals TodoSignals
if err := datastar.ReadSignals(r, &signals); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
}
newID := 0
sort.Slice(todoList, func(i, j int) bool {
return todoList[i].ID > todoList[j].ID
})
newID = todoList[0].ID + 1
newItem := TodoList{
ID: newID,
Name: signals.NewTodoName,
Completed: false,
}
todoList = append(todoList, newItem)
templ, _ := h.RenderS("fragments/todo-row", hrender.H{
"ID": newID,
"Name": newItem.Name,
"Completed": false,
})
sse := datastar.NewSSE(w, r)
if err := sse.PatchElements(templ,
datastar.WithModeReplace(),
datastar.WithSelectorID("new-todo-row"),
); err != nil {
http.Error(w, "bad request", http.StatusBadRequest)
return
}
signals.NewTodoName = ""
sse.MarshalAndPatchSignals(signals)
}))
mux.Handle("PATCH /todo/{id}/completed", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("patch todo triggered")
idStr := r.PathValue("id")
id, err := strconv.Atoi(idStr)
if err != nil {
http.Error(w, "invalid id", http.StatusBadRequest)
return
}
var foundItem *TodoList
for i := range todoList {
if todoList[i].ID == id {
todoList[i].Completed = true
foundItem = &todoList[i]
break
}
}
if foundItem == nil {
http.Error(w, "item not found", http.StatusNotFound)
return
}
templ, err := h.RenderS("fragments/todo-row", hrender.H{
"ID": foundItem.ID,
"Name": foundItem.Name,
"Completed": true,
})
if err != nil {
http.Error(w, "error loading template", http.StatusInternalServerError)
}
sse := datastar.NewSSE(w, r)
if err := sse.PatchElements(templ, datastar.WithModeOuter(),
datastar.WithSelectorID(fmt.Sprintf("todo-%d", id)),
); err != nil {
http.Error(w, "error", http.StatusInternalServerError)
}
}))
log.Println("server started on port 8080")
err := http.ListenAndServe(":8080", mux)
if err != nil {
panic(fmt.Sprintf("server cannot start %v", err))
}
}