Views
Views And Layouts
Render embedded html/template views through layouts with escaped data and helpers.
Embed views
The application embeds templates with ordinary Go embed.FS:
//go:embed views public
var Files embed.FS
var Views = lazyapp.MustSub(Files, "views")
Startup passes app.Views to lazyapp.New, which validates the renderer
before requests are served.
Name controller views
Controller view paths follow the controller and action:
app/views/<controller>/<action>.html.tpl
Given PostsController#Index, the default view is:
app/views/posts/index.html.tpl
An action can set data and return nil:
func (c *PostsController) Index(_ http.ResponseWriter, _ *http.Request) error {
c.Set("title", "Posts")
c.Set("posts", c.posts.List())
return nil
}
Read template data
Templates access values by the names passed to Set:
<h1>{{ .title }}</h1>
{{ range .posts }}
<a href="/posts/{{ .Param }}">{{ .Title }}</a>
{{ end }}
The same data map is available to the controller view and the selected layout.
Compose a layout
The default layout is:
app/views/layouts/app.html.tpl
GoLazy executes the controller view first, then supplies the rendered HTML as
.content:
<!doctype html>
<html>
<head>
<title>{{ .title }}</title>
</head>
<body>
<main>{{ .content }}</main>
</body>
</html>
Select another layout before rendering:
c.Layout("admin")
return nil
That resolves app/views/layouts/admin.html.tpl.
Register helpers
Application helpers are plain Go functions:
func RegisterHelpers() map[string]any {
return map[string]any{
"word_count": WordCount,
}
}
Startup passes them to the app:
Helpers: lazyapp.Helpers{helpers.RegisterHelpers()},
Templates call helpers by their registered names:
{{ word_count .post.Body }} words
Keep escaping explicit
Go's html/template escapes ordinary values. Convert only trusted renderer
output to template.HTML:
body, err := markdown.Convert(post.Body)
if err != nil {
return err
}
c.Set("body", template.HTML(body))
return nil
Use Forms for form helpers and Hotwire Turbo for frame rendering.