~repos /gromer

#golang#htmx#ssr

git clone https://pyrossh.dev/repos/gromer.git

gromer is a framework and cli to build multipage web apps in golang using htmx and alpinejs.


9eb20af1 Peter John

master HEAD

2 years ago
Update readme.md
64dabe3a Peter John

v1.3.1

3 years ago
Fix file download
ade61a29 Peter John

v1.3.0

3 years ago
Support File downloads

gromer

Version

gromer is a framework and cli to build multipage web apps in golang using htmx and alpinejs.

It uses a declarative syntax using inline jsx like templates for components and pages.

It also generates http handlers for your routes which follow a particular folder structure. Similar to other frameworks like nextjs, sveltekit.

You can install this extension vscode-go-inline-html to get syntax highlighting for these templates.

Requirements

Terminal window
go >= v1.18

Install

Terminal window
go get -u -v github.com/pyros2097/gromer/cmd/gromer

Using

You need to follow this directory structure similar to nextjs for the api route handlers to be generated and run the gromer command.

Example

These are some components

routes/todo.go

func Todo(c Context, todo *todos.Todo) *Node {
return c.Render(`
<li id="todo-{todo.ID}" class="{ completed: todo.Completed }">
<div class="view">
<form hx-target="#todo-{todo.ID}" hx-swap="outerHTML">
<input type="hidden" name="intent" value="complete" />
<input type="hidden" name="id" value="{todo.ID}" />
<input class="checkbox" type="checkbox" checked="{value}" />
</form>
<label>{todo.Text}</label>
<form hx-post="/" hx-target="#todo-{todo.ID}" hx-swap="delete">
<input type="hidden" name="intent" value="delete" />
<input type="hidden" name="id" value="{todo.ID}" />
<button class="destroy"></button>
</form>
</div>
</li>
`)
}

These are normal page routes

routes/get.go

type GetParams struct {
Page int `json:"page"`
Filter string `json:"filter"`
}
func GET(c Context, params GetParams) (*Node, int, error) {
c.Meta("title", "Gromer Todos")
c.Meta("description", "Gromer Todos")
c.Meta("author", "gromer")
c.Meta("keywords", "gromer")
return c.Render(`
<div class="todoapp">
<header class="header">
<h1>todos</h1>
<form hx-post="/" hx-target="#todo-list" hx-swap="afterbegin" _="on htmx:afterOnLoad set #text.value to ''">
<input type="hidden" name="intent" value="create" />
<input class="new-todo" id="text" name="text" placeholder="What needs to be done?" autofocus="false" autocomplete="off" />
</form>
</header>
<section class="main">
<input class="toggle-all" id="toggle-all" type="checkbox" />
<label for="toggle-all">Mark all as complete</label>
<TodoList id="todo-list" page="{params.Page}" filter="{params.Filter}" />
</section>
<footer class="footer">
<TodoCount filter="{params.Filter}" />
<ul class="filters">
<li>
<a href="?filter=all">All</a>
</li>
<li>
<a href="?filter=active">Active</a>
</li>
<li>
<a href="?filter=completed">Completed</a>
</li>
</ul>
<form hx-target="#todo-list" hx-post="/">
<input type="hidden" name="intent" value="clear_completed" />
<button type="submit" class="clear-completed" >Clear completed</button>
</form>
</footer>
</div>
`), 200, nil
}

And then run the gromer cli command annd it will generate the route handlers in a main.go file,

main.go

// Code generated by gromer. DO NOT EDIT.
package main
import (
"github.com/gorilla/mux"
"github.com/pyros2097/gromer"
"github.com/pyros2097/gromer/assets"
"github.com/pyros2097/gromer/gsx"
"github.com/rs/zerolog/log"
"gocloud.dev/server"
"github.com/pyros2097/gromer/_example/assets"
"github.com/pyros2097/gromer/_example/components"
"github.com/pyros2097/gromer/_example/containers"
"github.com/pyros2097/gromer/_example/routes/404"
"github.com/pyros2097/gromer/_example/routes"
"github.com/pyros2097/gromer/_example/routes/about"
)
func init() {
gsx.RegisterComponent(components.Todo, "todo")
gsx.RegisterComponent(components.Checkbox, "value")
gsx.RegisterComponent(containers.TodoCount, "filter")
gsx.RegisterComponent(containers.TodoList, "page", "filter")
}
func main() {
baseRouter := mux.NewRouter()
baseRouter.Use(gromer.LogMiddleware)
baseRouter.NotFoundHandler = gromer.StatusHandler(not_found_404.GET)
staticRouter := baseRouter.NewRoute().Subrouter()
staticRouter.Use(gromer.CacheMiddleware)
gromer.StaticRoute(staticRouter, "/gromer/", gromer_assets.FS)
gromer.StaticRoute(staticRouter, "/assets/", assets.FS)
gromer.StylesRoute(staticRouter, "/styles.css")
pageRouter := baseRouter.NewRoute().Subrouter()
gromer.Handle(pageRouter, "GET", "/", routes.GET)
gromer.Handle(pageRouter, "POST", "/", routes.POST)
gromer.Handle(pageRouter, "GET", "/about", about.GET)
log.Info().Msg("http server listening on http://localhost:3000")
srv := server.New(baseRouter, nil)
if err := srv.ListenAndServe(":3000"); err != nil {
log.Fatal().Stack().Err(err).Msg("failed to listen")
}
}

TODO:

Add inline css formatting

Add inline html formatting