Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/blakewilliams/glam
Go's `html/template` with a component focus
https://github.com/blakewilliams/glam
Last synced: 16 days ago
JSON representation
Go's `html/template` with a component focus
- Host: GitHub
- URL: https://github.com/blakewilliams/glam
- Owner: BlakeWilliams
- Created: 2024-09-02T16:16:56.000Z (4 months ago)
- Default Branch: main
- Last Pushed: 2024-10-21T00:27:40.000Z (2 months ago)
- Last Synced: 2024-12-15T14:57:10.508Z (19 days ago)
- Language: Go
- Homepage:
- Size: 98.6 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Glam
Glam is an attempt to make Go templates more component focused using the constraints of the existing Go language and tooling.
With glam, you can write templates like:
```html
Sign Up
```
With structs backing the logic and templates:
```go
type FormComponent struct {
Action string `attr:"action"`
Children template.HTML
Class string `attr:"class"`
}
``````html
{{.Children}}
```
## Usage
Glam takes an approach similar to [ViewComponent](https://viewcomponent.org/), using sidecar templates to define components. In addition to coupling Go templates with structs, glam also allows enables a React style syntax for utilizing components in your templates.
```go
import "github.com/blakewilliams/glam"type GreetPage struct {
Name string
}// a Helper method we can use in our template
func (g GreetPage) YellName() string {
return strings.ToUpper(g.Name)
}// Then, to render the template:
engine := glam.New(nil)
engine.RegisterComponent(GreetPage{}, `Hello, {{.YellName}}`)var b strings.Builder
engine.Render(&b, &GreetPage{Name: "World"})
```The `GreetPage` struct instance will be passed to the `greet_page.html` template as `data`, allowing you to access public fields and call methods on the struct.
### Composing components
Let's say we want to reuse our YellName functionality in another component, but also **bold** the name. We can create a new component and reference the component directly in our `greet_page.html` template as if it was another element:
```go
type Yell struct {
Name string
}func (y Yell) YellName() string {
return strings.ToUpper(y.Name)
}// Lets update our GreetPage component to use our new Yell component
engine.RegisterComponent(GreetPage{}, `Hello, `)
// Let's also register our new Yell component
engine.RegisterComponent(Yell{}, `{{.YellName}}`)
```The HTML is parsed and the `Yell` HTML tag is replaced with a call to render our Yell component.
### Child content
Since components can be used like HTML tags, that means they can have child content too. The current approach is relatively basic since it always expects a `template.HTML` value, but you can accept and render child content using the conventional `Children` struct field:
```go
type WrapperComponent struct {
Children template.HTML
}
``````html
Hello
```When the template above is executed, `WrapperComponent` will have `Children` populated with the HTML safe string `Hello`.
### Request specific data
Glam templates can utilize request specific data via `RenderWithFuncs`:
```go
engine := glam.New(glam.FuncMap{
"CSRF": func() string {
// assuming request is in scope and has a CSRFToken method
panic("must be overridden")
},
})
engine.RegisterComponent(&LoginForm{}, "")var b strings.Builder
engine.RenderWithFuncs(&b, &LoginForm{}, glam.FuncMap{
"CSRF": func() string {
// assuming request is in scope and has a CSRFToken method
return request.CSRFToken()
},
})
````### Graceful degradation
Components can implement the `Recoverable` interface to rescue against `panic`s and render fallback content. For example:
```go
type SafeSidebar struct {
Children template.HTML
}func (mc *SafeSidebar) Recover(w io.Writer, err any) {
w.Write(`Failed to load sidebar`)
}
```If any `panic` occurs when rendering `SafeSidebar` or child content (via `foo bar`) it will render the fallback content written.