Todo

Source Code: https://github.com/stackus/goht-todo

A simple Todo application that demonstrates the capabilities of GoHT.

GoHT Todo GoHT Todo

HTMX

This application also makes use of HTMX to provide a more interactive experience. HTMX is a library that allows you to create web applications with less JavaScript. It uses HTML attributes to define the behavior of the application.

Page Layouts

Syntax Highlighting

The GoHT templates lack any syntax highlighting in the website. This is a known issue that is being worked on.

Each page will be wrapped with the following main layout:

package shared

@goht Page(title string) {
!!!
%html.h-full{lang: "en"}
	%head
		%meta{charset: "UTF-8"}
		%title= title
		%link{rel: "icon", type: "image/svg+xml", href: "/dist/favicon.svg"}
		%meta{content: "width=device-width, initial-scale=1", name: "viewport"}
		%meta{content: "index, follow", name: "robots"}
		%meta{content: "7 days", name: "revisit-after"}
		%meta{content: "English", name: "language"}
		%script{src: "https://unpkg.com/htmx.org@1.9.10", integrity: "sha384-...", crossorigin: "anonymous"}
		%script{src: "https://unpkg.com/hyperscript.org@0.9.12"}
		%script{src: "https://unpkg.com/sortablejs@1.15.0"}
		%script{src: "/dist/app.js"}
		%link{rel: "stylesheet", href: "/dist/styles.css"}
	%body.h-full.bg-yellow-50.font-mono
		%section.max-w-lg.mx-auto.my-2
			%h1.text-8xl.font-black.text-center.m-0.pb-2 Todos
			=@children
}

Using =@children makes it straightforward to build nested layouts. Here is an example of one of the pages:

package pages

import (
	"github.com/stackus/goht-todos/domain"
	"github.com/stackus/goht-todos/templates/partials"
	"github.com/stackus/goht-todos/templates/shared"
)

@goht HomePage(todos []domain.Todo) {
=@render shared.Page("Home")
	=@render partials.Search("")
	=@render partials.RenderTodos(todos)
	=@render partials.AddTodoForm()
}

Each page in the application makes use of the base template and will then include additional content which is rendered by the before mentioned =@children directive.

Taking a look at the partials.AddTodoForm template:

package partials

@goht AddTodoForm() {
%form.inline{
	method: "POST",
	action: "/todos",
	hx-post: "/todos",
	hx-target: "#no-todos",
	hx-swap: "beforebegin",
}
	%label.flex.items-center
		%span.text-lg.font-bold Add Todo
		%input.ml-2.grow{
			type: "text",
			name: "description",
			_: "on keyup if the event's key is 'Enter' set my value to '' trigger keyup"
		}
}

Being able to put the attributes across multiple lines makes it easier to read and understand the structure of the form. The HTMX attributes are also included without any additional effort. Likewise, we can also add the HyperScript code with _ as its name.

Handlers

A simple render() function is used to render the templates and catch any errors that may occur:

func render(ctx context.Context, template goht.Template, w io.Writer) error {
	err := template.Render(ctx, w)
	if err != nil {
		return errors.ErrInternal.Wrap(err, "failed to render template")
	}
	return nil
}

This is the handler that returns the content when a Todo is being modified:

func handleGet(todos domain.TodosStore) http.HandlerFunc {
	return errorHandler(func(w http.ResponseWriter, r *http.Request) error {
		id := chi.URLParam(r, "todoId")

		todo, err := todos.Get(r.Context(), id)
		if err != nil {
			return err
		}

		if hx.IsHtmx(r) {
			return render(r.Context(), partials.EditTodoForm(todo), w)
		}

		return render(r.Context(), pages.TodoPage(todo), w)
	})
}

Checkout the application’s source code to see more examples of how GoHT is used to build the application.