Velvet is a templating package for Go. It bears a striking resemblance to "handlebars" based templates, there are a few small changes/tweaks, that make it slightly different.
If you know handlebars, you basically know how to use Velvet.
Let's assume you have a template (a string of some kind):
Given that string, you can render the template like such:
ctx := velvet.NewContext()
ctx.Set("name", "Mark")
ctx.Set("names", []string{"John", "Paul", "George", "Ringo"})
s, err := velvet.Render(input, ctx)
if err != nil {
// handle errors
}
Which would result in the following output:
<h1>Mark</h1>
<ul>
<li>John</li>
<li>Paul</li>
<li>George</li>
<li>Ringo</li>
</ul>
What to do? Should you render the content, or not? Using Velvet's built in if
, else
, and unless
helpers, let you figure it out for yourself.
Into everyone's life a little looping must happen. We can't avoid the need to write loops in applications, so Velvet helps you out by coming loaded with an each
helper to iterate through arrays
, slices
, and maps
.
When looping through arrays
or slices
, the block being looped through will be access to the "global" context, as well as have four new variables available within that block:
@first
[bool
] - is this the first pass through the iteration?@last
[bool
] - is this the last pass through the iteration?@index
[int
] - the counter of where in the loop you are, starting with0
.@value
- the current element in the array or slice that is being iterated over.
By using "block parameters" you can change the "key" of the element being accessed from @value
to a key of your choosing.
To change both the key and the index name you can pass two "block parameters"; the first being the new name for the index and the second being the name for the element.
Looping through maps
using the each
helper is also supported, and follows very similar guidelines to looping through arrays
.
@first
[bool
] - is this the first pass through the iteration?@last
[bool
] - is this the last pass through the iteration?@key
- the key of the pair being accessed.@value
- the value of the pair being accessed.
By using "block parameters" you can change the "key" of the element being accessed from @value
to a key of your choosing.
To change both the key and the value name you can pass two "block parameters"; the first being the new name for the key and the second being the name for the value.
json
- returns a JSON marshaled string of the value passed to it.js_escape
- safely escapes a string to be used in a JavaScript bit of code.html_escape
- safely escapes a string to be used in an HTML bit of code.upcase
- upper cases the entire string passed to it.downcase
- lower cases the entire string passed to it.markdown
- converts markdown to HTML.len
- returns the length of an array or slice
Velvet also imports all of the helpers found https://github.com/markbates/inflect/blob/master/helpers.go
No templating package would be complete without allowing for you to build your own, custom, helper functions.
The first thing to understand about building custom helper functions is their are a few "valid" return values:
Return just a string
. The string
will be HTML escaped, and deemed "not"-safe.
func() string {
return ""
}
Return a string
and an error. The string
will be HTML escaped, and deemed "not"-safe.
func() (string, error) {
return "", nil
}
https://golang.org/pkg/html/template/#HTML
Return a template.HTML
string. The template.HTML
will not be HTML escaped, and will be deemed safe.
func() template.HTML {
return template.HTML("")
}
Return a template.HTML
string and an error. The template.HTML
will not be HTML escaped, and will be deemed safe.
func() ( template.HTML, error ) {
return template.HTML(""), error
}
Custom helper functions can take any type, and any number of arguments. There is an option last argument, velvet.HelperContext
, that can be received. It's quite useful, and I would recommend taking it, as it provides you access to things like the context of the call, the block associated with the helper, etc...
Custom helpers can be registered in one of two different places; globally and per template.
err := velvet.Helpers.Add("greet", func(name string) string {
return fmt.Sprintf("Hi %s!", name)
})
if err != nil {
// handle errors
}
The greet
function is now available to all templates that use Velvet.
s, err := velvet.Render(`<h1>{{greet "mark"}}</h1>`, velvet.NewContext())
if err != nil {
// handle errors
}
fmt.Print(s) // <h1>Hi mark!</h1>
t, err := velvet.Parse(`<h1>{{greet "mark"}}</h1>`)
if err != nil {
// handle errors
}
t.Helpers.Add("greet", func(name string) string {
return fmt.Sprintf("Hi %s!", name)
})
if err != nil {
// handle errors
}
The greet
function is now only available to the template it was added to.
s, err := t.Exec(velvet.NewContext())
if err != nil {
// handle errors
}
fmt.Print(s) // <h1>Hi mark!</h1>
Like the if
and each
helpers, block helpers take a "block" of text that can be evaluated and potentially rendered, manipulated, or whatever you would like. To write a block helper, you have to take the velvet.HelperContext
as the last argument to your helper function. This will give you access to the block associated with that call.
velvet.Helpers.Add("upblock", func(help velvet.HelperContext) (template.HTML, error) {
s, err := help.Block()
if err != nil {
return "", err
}
return strings.ToUpper(s), nil
})
s, err := velvet.Render(`{{#upblock}}hi{{/upblock}}`, velvet.NewContext())
if err != nil {
// handle errors
}
fmt.Print(s) // HI