Pongo2 is a Django-like syntax template engine for Go. It was created by Florian Schlachter. Echo ViewKit uses a forked version of Pongo2, extensively customizing it to meet the demands of modern web development and seamless Echo integration.
Pongo2 syntax is similar to the Django template language. Currently, we do not cover Pongo2 syntax in this documentation. However, you can find detailed specifications at the following links:
Echo ViewKit provides an Echo renderer implementation integrated with the Pongo2 template engine. The renderer is an interface that allows you to render templates in Echo applications. Here is a simple example of how to use the renderer:
// Create a new Echo instance.
e := echo.New()
// Create a new Echo ViewKit instance.
v := viewkit.New()
// Set the base directory of the template files.
v.BaseDir = "views"
// Create a new renderer and set it to the Echo instance.
e.Renderer = v.MustRenderer()
After setting up the renderer, you can call the Render
method in handler functions to render a template:
func HelloHandler(c echo.Context) error {
return c.Render(http.StatusOK, "hello", map[string]any{
"message": "Hello World",
})
}
The first argument of the Render
method is the HTTP status code,
the second argument is the template file name (You can omit the .html
file extension),
and the third argument is the data to pass to the template.
When the view renderer renders the templates, they are compiled and cached by default.
This improves rendering performance but it can be inconvenient during development because updates to template files are not immediately reflected.
To disable caching, set the Debug
property of the ViewKit instance to true
:
v := viewkit.New()
v.Debug = true
As you saw in the previous examples, you can pass data to the template by providing a map[string]any
map.
c.Render(http.StatusOK, "hello", map[string]any{
"message": "Hello World",
})
This data can be accessed in the template using the key names.
<h1>{{ message }}</h1>
If you prefer to use typed values instead of a map, you can use a struct with the pongo2
tag.
type HelloProps struct {
Message string `pongo2:"message"`
}
func HelloHandler(c echo.Context) error {
return c.Render(http.StatusOK, "hello", &HelloProps{
Message: "Hello World",
})
}
The simplest configuration of the view renderer is just to set the BaseDir
property.
It is the directory path where the template files are located.
v := viewkit.New()
v.BaseDir = "views"
You can also load templates from Go fs.FS
interface instead of the directory path.
It is useful when you want to embed template files into a binary.
Echo ViewKit provides the FS
property to set the fs.FS
interface.
import (
"embed"
viewkit "github.com/kohkimakimoto/echo-viewkit"
)
//go:embed views
var viewFS embed.FS
func main() {
v := viewkit.New()
v.FS = viewFS
v.FSBaseDir = "views"
// ...
}
Sometimes you want to access the same data or functions in all templates. The shared context is useful for these cases.
📝 As explained in the Components section, encapsulated components have their own context, meaning that data or functions from the parent context are not accessible. The shared context provides a mechanism to share data or functions across all templates, including components.
To use the shared context, you may set the SharedContextProviders
property.
v := viewkit.New()
v.SharedContextProviders = map[string]viewkit.SharedContextProviderFunc{
"siteName": func(c echo.Context) (any, error) {
return "Echo ViewKit Example", nil
},
}
As you can see in the example above, SharedContextProviders
is a map of string
and SharedContextProviderFunc
.
The SharedContextProviderFunc
is a function that returns shared data to be passed to all templates when rendering.
This function is called whenever the renderer renders a template, ensuring the shared data is available.
You can access the shared data in any template by using the key names.
<h1>{{ siteName }}</h1>
Returning a function from a SharedContextProviderFunc
is a good practice because it enables lazy evaluation of the shared data,
reducing the overhead associated with the shared context.
v := viewkit.New()
v.SharedContextProviders = map[string]viewkit.SharedContextProviderFunc{
"siteName": func(c echo.Context) (any, error) {
return func() string {
return "Echo ViewKit Example"
}, nil
},
}
{# You can call the function like this. #}
<h1>{{ siteName() }}</h1>
{# Or you can still access like a value. #}
{# Because you can omit the brackets when calling a function without arguments. #}
<h1>{{ siteName }}</h1>
Echo ViewKit has several standard shared context providers and automatically sets them up by default. You can use the following functions in any template without any additional configuration.
Here are the functions provided by standard shared context providers:
A function that returns true
if you set Echo ViewKit's Debug
property to true
.
{% if is_debug %}
<p>This is a debug mode.</p>
{% endif %}
A function that returns the path of the current URL.
<p>{{ url_path }}</p> {# => /path/to/current #}
A function that returns the query string of the current URL.
<p>{{ url_query }}</p> {# => key1=value1&key2=value2&key3=value3 #}
<p>{{ url_query("key1") }}</p> {# => value1 #}
A function that returns the path and query string of the current URL.
<p>{{ url_path_query }}</p> {# => /path/to/current?key1=value1&key2=value2&key3=value3 #}
<p>{{ url_path_query("key1") }}</p> {# => /path/to/current?key1=value1 #}
<p>{{ url_path_query("key1", "key2") }}</p> {# => /path/to/current?key1=value1&key2=value2 #}
A function that returns the JSON string of the passed value.
<div data-json="{{ json_marshal(value) }}"></div>
If you don't want to automatically apply the standard shared context providers,
you can disable them by setting the DisableStandardSharedContextProviders
property to true
.
v := viewkit.New()
v.DisableStandardSharedContextProviders = true
You can also share the data passed to the Render
method by registering key names to the SharedContextKeys
property.
v := viewkit.New()
v.SharedContextKeys = []string{"message"}
If you pass the data with the key name message
, it can be accessed in all templates.
c.Render(http.StatusOK, "hello", map[string]any{
"message": "Hello World",
})
Sometimes, you don't need a dedicated handler function for a view template.
In that case, you can use the ViewHandler
to render the template directly.
e.GET("/", viewkit.ViewHandler("pages/index"))
If you want to pass data to the template, you can use the ViewHandlerWithData
function.
e.GET("/", viewkit.ViewHandlerWithData("pages/index", map[string]any{
"message": "Hello World",
}))
You can render a fragment of a template using the fragment
template tag.
This is especially useful when working with frontend frameworks like htmx, as these types of frameworks load portions of the page dynamically.
In your template, you can define a fragment like this:
{% fragment "user-list" %}
<ul>
{% for user in users %}
<li>{{ user.Name }}</li>
{% endfor %}
</ul>
{% endfragment %}
Then, you can render only the fragment by specifying its name after the template name, using the #
separator, like this:
c.Render(http.StatusOK, "index#user-list", map[string]any{
"users": users,
})