I was tired of having the same issues phase when starting a new web project with Go's html/template.
tpl is an opinionated lightweight helper library that makes structuring, parsing, and rendering templates in Go more tolerable. It also adds small helpers, translations, and i18n functions to the funcmap of templates.
Table of content:
- Installation
- Usage
- Example templates
- i18n
- Passing a funcmap
- Built-in functions#built-in-functions
To use this library, you'll need to adopt the following files and directory structure for your templates:
Create a templates directory with the following structure:
Now app.html and layout.html are example names, you name your layout the way you want.
Layouts are HTML files at the root of your templates/ directory. They contain blocks that your views will fill. You may name them as you want but they must have a sub-directory in the views directory with their name without the .html.
views directory contains one directory per layout file name without the .html extension. If you have three layout templates, public.html, app.html, and xyz.html, you'll have three sub-directories in the Views directory, each containing the views for this layout. So views/public, views/app, and views/xyz.
partials is a directory where you put all re-usable pieces of template you need to embed into your HTML pages. For instance, you embed a item-list.html in 'views/xyz/list.html', views/app/mylist.html, and views/xyz/admin-list.html pages. All three can use the partial.
Note: This directory was named _partials before, please rename it to partials to remove the obsolete warning.
emails directory is used for your emails, those HTML templates does not have a base layout and are used as-is in terms of rendering.
translations directory is where you put message translations via one file named after the language. It's optional, if you don't need translations you don't need to create this directory.
You'll need to parse your templates at the start of your program. The library returns a tpl.Template structure that you use to render your pages and emails.
For example:
Note: Previously it was required to wrap your template data into tpl.PageData structure. It's not required anymore, although tpl still exposes PageData you can use or embed into your own structure.
For new project I tend to use tpl.PageData like this:
And for existing project, I tend to embed tpl.PageData into my existing structure, so my HTML templates does not change much.
This structure is there if you need it. It just have a sane list of fields that most web application are using, you can use it or not, it's really up to you.
Here's the fields of the tpl.PageData:
Lang and Locale are useful if you want to use the i18n feature.
CurrentUser is handy if you want to let your templates know about the current user.
Env is useful if your system has multiple environment, like dev, staging, prod and you'd want to do different things based on the env. I personally use if to have a non-minified JavaScript bundle in dev and staging, while a minified one in prod.
Extra can be useful for anything that your views need that's not present in the main Data field.
Title is also helpful to set the page title, you can have this in your layout templates:
Alert can be use to display flash message to the user, errors and successes etc. The Notification structure is this:
Usually I have a web package with a render.go that handles and exposes a Render function, here's an example:
This is a real-world example, I'm embedding tpl.PageData into an existing structure even if it's repeating some field as my existing HTML template were already using {{ .Language }} and tpl.PageData have a Lang field.
And yes, I was too lazy to replace all .Language.
So it's really flexible if you either use it as-is or embed into an existing structure for existing HTML templates.
I'm using it like this in an handler:
The fact that my web.Render functions accept a variadic arguments I'm able to use the function somewhat relative to what happened in the handler. If there's no alert, I only pass the data, if there's no data, I just render the page.
**This is just an **example, you can shape it the way you prefer. This library only facilitate the structuring, parsing, and rendering of templates.
The tests use somewhat real-ish directories and file structures from which you may get started.
Look at the testdata directory. In your program, you might want to name the root directory templates but it's configurable.
templates/layout.html:
templates/views/layout/home.html:
templates/_partials/nav.html:
There's nothing really special regarding emails, other than tpl handles their rendering directly, once you have call the Parse function you may render any email template like so:
Your templates in templates/emails can access all built-in functions and will also have the same funcmap as your HTML templates.
If your web application needs multilingual support, you can create language message files and save them in the Translations directory.
templates/translations/en.json:
The translation functions expect a language as first argument. This is where the tpl.PageData may come handy if you use it directly or embed it in your structure.
Inside your templates:
There's helper function to display dates and currencies in the proper format based on Locale.
And inside the templates/views/layout/home.html file:
Display: The price is 59.99 $
If Locale is en-US: The price is $55.99.
There's also a {{ shortdate .Locale .Data.CreatedAt }} helper function which formats a time.Time properly based on Locale.
NOTE: At this time there's only a limited amount of locale supported. If your locale isn't supported, please consider contributing the changes.
Translation functions are also exposes, so tpl.Translate can be call from your backend if you need translation outside of HTML templates.
You may have helper functions you'd like to pass to the templates. Here's how:
tpl adds the following functions to the funcmap.
map | Create a map, useful to pass data to another template |
iterate | Allow you to iterate X numbers of time |
xsrf | Render an hidden input for your XSRF token |
cut | Remove chars from a string |
default | Display a fallback if input is nil or zero |
filesize | Display bytes size in KB, MB, GB, etc |
slugify | Turn a string into a slug |
intcomma | Adds , to thousands |
naturaltime | Display X minutes ago kind of output |
Look at the testdata/views/app/dashboard.html for usage examples of these functions.