Core Framework
Views and Layouts
Render embedded html/template views through layouts with escaped data and explicit trusted HTML.
Embed views
The application embeds its templates:
//go:embed views public
var Files embed.FS
app.Views returns the views subtree as an fs.FS. Startup passes that
filesystem to lazyapp.New, which initializes the renderer.
The renderer requires a default layout:
layouts/app.html.tpl
Missing embedded resources fail during application initialization rather than on the first production request.
Name views by controller and action
Controller view paths follow:
app/views/<controller>/<action>.html.tpl
Given a request routed to PostsController, this call:
return c.Render("index")
loads:
posts/index.html.tpl
Set template data
Add values before rendering:
c.Set("title", "Posts")
c.Set("posts", c.posts.List())
Templates access them by name:
<h1>{{ .title }}</h1>
{{ range .posts }}
<a href="/posts/{{ .Slug }}">{{ .Title }}</a>
{{ end }}
The same data map is available to the layout.
Compose the layout
GoLazy executes the controller view first. It then passes the generated content
to the selected layout as .content:
<!doctype html>
<html>
<head>
<title>{{ .title }}</title>
</head>
<body>
<main>{{ .content }}</main>
</body>
</html>
Use SetLayout before Render to select a layout other than app.
Register helpers
Application helpers are plain Go functions. The sample app keeps them in
app/helpers and returns them as a map:
func RegisterHelpers() map[string]any {
return map[string]any{
"word_count": WordCount,
"read_time": ReadTime,
}
}
Startup passes that map to lazyapp.New:
return lazyapp.New(lazyapp.Config{
Name: "sample_app",
Drawer: Draw,
Public: app.Public,
Views: app.Views,
Context: Context,
Helpers: []map[string]any{helpers.RegisterHelpers()},
})
Templates call registered helpers by name:
{{ word_count .post.Body }} words
{{ read_time .post.Body }} min read
Router helpers such as path_for are registered automatically by lazyapp.
Escaping and trusted HTML
Go's html/template escapes ordinary data according to its HTML context. Keep
user and application data as strings whenever possible.
Only convert HTML that was produced by a trusted renderer:
body, err := markdown.Convert(post.Body)
if err != nil {
return fmt.Errorf("render post markdown: %w", err)
}
c.Set("body", template.HTML(body))
template.HTML disables escaping. Never use it merely to make untrusted input
render as markup.
Rendering errors
Render returns contextual errors for:
- Missing view files.
- Invalid view templates.
- View execution failures.
- Missing or invalid layouts.
- Layout execution failures.
- Response write failures.
Return those errors from the action so the framework can produce the HTTP response.