The quick and dirty way of rendering a handlebars template:
{{title}}
{{body}}
`
ctx := map[string]string{
"title": "My New Post",
"body": "This is my first post!",
}
result, err := handlebars.Render(tpl, ctx)
if err != nil {
panic("Please report a bug :)")
}
fmt.Print(result)
}
">
package main
import (
"fmt""github.com/flowchartsman/handlebars/v3"
)
funcmain() {
tpl:=`
{{title}}
{{body}}
`ctx:=map[string]string{
"title": "My New Post",
"body": "This is my first post!",
}
result, err:=handlebars.Render(tpl, ctx)
iferr!=nil {
panic("Please report a bug :)")
}
fmt.Print(result)
}
Displays:
My New Post
This is my first post!
">
<divclass="entry"><h1>My New Posth1><divclass="body">
This is my first post!
div>div>
Please note that the template will be parsed everytime you call Render() function. So you probably want to read the next section.
Correct Usage
To avoid parsing a template several times, use the Parse() and Exec() functions:
{{title}}
{{body}}
`
ctxList := []map[string]string{
{
"title": "My New Post",
"body": "This is my first post!",
},
{
"title": "Here is another post",
"body": "This is my second post!",
},
}
// parse template
tpl, err := handlebars.Parse(source)
if err != nil {
panic(err)
}
for _, ctx := range ctxList {
// render template
result, err := tpl.Exec(ctx)
if err != nil {
panic(err)
}
fmt.Print(result)
}
}
">
package main
import (
"fmt"
"github.com/flowchartsman/handlebars/v3
)
funcmain() {
source:=`
{{title}}
{{body}}
`ctxList:= []map[string]string{
{
"title": "My New Post",
"body": "This is my first post!",
},
{
"title": "Here is another post",
"body": "This is my second post!",
},
}
// parse templatetpl, err:=handlebars.Parse(source)
iferr!=nil {
panic(err)
}
for_, ctx:=rangectxList {
// render templateresult, err:=tpl.Exec(ctx)
iferr!=nil {
panic(err)
}
fmt.Print(result)
}
}
Displays:
My New Post
This is my first post!
Here is another post
This is my second post!
">
<divclass="entry"><h1>My New Posth1><divclass="body">
This is my first post!
div>div><divclass="entry"><h1>Here is another posth1><divclass="body">
This is my second post!
div>div>
You can use MustParse() and MustExec() functions if you don't want to deal with errors:
The rendering context can contain any type of values, including array, slice, map, struct and func.
When using structs, be warned that only exported fields are accessible. However you can access exported fields in template with their lowercase names. For example, both {{author.firstName}} and {{Author.FirstName}} references give the same result, as long as Author and FirstName are exported struct fields.
More, you can use the handlebars struct tag to specify a template variable name different from the struct field name.
By {{author.firstName}} {{author.lastName}}
{{body}}
Comments
{{#each comments}}
By {{author.firstName}} {{author.lastName}}
{{content}}
{{/each}}
`
type Person struct {
FirstName string
LastName string
}
type Comment struct {
Author Person
Body string `handlebars:"content"`
}
type Post struct {
Author Person
Body string
Comments []Comment
}
ctx := Post{
Person{"Jean", "Valjean"},
"Life is difficult",
[]Comment{
Comment{
Person{"Marcel", "Beliveau"},
"LOL!",
},
},
}
output := handlebars.MustRender(source, ctx)
fmt.Print(output)
}
">
package main
import (
"fmt"
"github.com/flowchartsman/handlebars/v3
)
funcmain() {
source:=`
<divclass="entry"><h1>All about <p> Tagsh1><divclass="body"><p>This is a post about <p> tagsp>div>div>
When returning HTML from a helper, you should return a SafeString if you don't want it to be escaped by default. When using SafeString all unknown or unsafe data should be manually escaped with the Escape method.
" + handlebars.Escape(text) + "")
})
tpl := handlebars.MustParse("{{link url text}}")
ctx := map[string]string{
"url": "http://www.aymerick.com/",
"text": "This is a cool website",
}
result := tpl.MustExec(ctx)
fmt.Print(result)
">
Those built-in helpers are available to all templates.
The if block helper
You can use the if helper to conditionally render a block. If its argument returns false, nil, 0, "", an empty array, an empty slice or an empty map, then handlebars will not render the block.
When using a block expression, you can specify a template section to run if the expression returns a falsy value. That section, marked by {{else}} is called an "else section".
You can use the unless helper as the inverse of the if helper. Its block will be rendered if the expression returns a falsy value.
{{#unless license}}
WARNING: This entry does not have a license!
{{/unless}}
">
<divclass="entry">
{{#unless license}}
<h3class="warning">WARNING: This entry does not have a license!h3>
{{/unless}}
div>
The each block helper
You can iterate over an array, a slice, a map or a struct instance using this built-in each helper. Inside the block, you can use this to reference the element being iterated over.
You can optionally provide an {{else}} section which will display only when the passed argument is an empty array, an empty slice or an empty map (a struct instance is never considered empty).
The lookup helper allows for dynamic parameter resolution using handlebars variables.
{{#each bar}}
{{lookup ../foo @index}}
{{/each}}
The log helper
The log helper allows for logging while rendering a template.
{{log "Look at me!"}}
Note that the handlebars.js @level variable is not supported.
The equal helper
The equal helper renders a block if the string version of both arguments are equals.
For example that template:
{{#equal foo "bar"}}foo is bar{{/equal}}
{{#equal foo baz}}foo is the same as baz{{/equal}}
{{#equal nb 0}}nothing{{/equal}}
{{#equal nb 1}}there is one{{/equal}}
{{#equal nb "1"}}everything is stringified before comparison{{/equal}}
Note that as the first parameter of the helper is typed as bool an automatic conversion is made if corresponding context value is not a boolean. So this helper works with that context too:
ctx:=map[string]interface{}{"yep": "message"}
Here, "message" is converted to true because it is an non-empty string. See IsTrue() function for more informations on boolean conversion.
Else Block Evaluation
We can enhance the si block helper to evaluate the else block by calling options.Inverse() if conditional is false:
Actually, handlebars perfoms automatic string conversion. So because the first parameter of the helper is typed as string, the first argument will be converted from the 10 integer to "10", and the helper outputs:
10 VALJEAN
Note that this kind of automatic conversion is done with bool type too, thanks to the IsTrue() function.
Options Argument
If a helper needs the Options argument, just add it at the end of helper parameters:
Thanks to the options argument, helpers have access to the current evaluation context, to the Hash arguments, and they can manipulate the private data variables.
The Options argument is even necessary for Block Helpers to evaluate block and "else block".
Context Values
Helpers fetch current context values with options.Value() and options.ValuesStr().
Value() returns an interface{} and lets the helper do the type assertions whereas ValueStr() automatically converts the value to a string.
Helpers can get the full hash with options.Hash() that returns a map[string]interface{}.
Private Data
Helpers access private data variables with options.Data() and options.DataStr().
Data() returns an interface{} and lets the helper do the type assertions whereas DataStr() automatically converts the value to a string.
Helpers can get the entire current data frame with options.DataFrame() that returns a *DataFrame.
For helpers that need to inject their own private data frame, use options.NewDataFrame() to create the frame and options.FnData() to evaluate the block with that frame.
For example:
source:=`{{#voodoo kind=a}}Voodoo is {{@magix}}{{/voodoo}}`ctx:=map[string]interface{}{
"a": "awesome",
}
handlebars.RegisterHelper("voodoo", func(options*handlebars.Options) string {
// create data frame with @magix dataframe:=options.NewDataFrame()
frame.Set("magix", options.HashProp("kind"))
// evaluates block with new data framereturnoptions.FnData(frame)
})
Helpers that need to evaluate the block with a private data frame and a new context can call options.FnCtxData().
Utilites
In addition to Escape(), handlebars provides utility functions that can be usefull for helpers.
Str()
Str() converts its parameter to a string.
Booleans:
handlebars.Str(3) +" foos and "+handlebars.Str(-1.25) +" bars"// Outputs: "3 foos and -1.25 bars"
Numbers:
"everything is "+handlebars.Str(true) +" and nothing is "+handlebars.Str(false)
// Outputs: "everything is true and nothing is false"
Custom data can be passed to partials through hash parameters.
For example:
myPartial name=hero }}")
tpl.RegisterPartial("myPartial", "My hero is {{name}}")
ctx := map[string]interface{}{
"hero": "Goldorak",
}
result := tpl.MustExec(ctx)
fmt.Print(result)
">
tpl:=handlebars.MustParse("{{> myPartial name=hero }}")
tpl.RegisterPartial("myPartial", "My hero is {{name}}")
ctx:=map[string]interface{}{
"hero": "Goldorak",
}
result:=tpl.MustExec(ctx)
fmt.Print(result)
Displays:
My hero is Goldorak
Utility Functions
You can use following utility fuctions to parse and register partials from files:
ParseFile() - reads a file and return parsed template
Template.RegisterPartialFile() - reads a file and registers its content as a partial with given name
Template.RegisterPartialFiles() - reads several files and registers them as partials, the filename base is used as the partial name
Mustache
Handlebars is a superset of mustache but it differs on those points:
Alternative delimiters are not supported
There is no recursive lookup
Limitations
These handlebars options are currently NOT implemented:
compat - enables recursive field lookup
knownHelpers - list of helpers that are known to exist (truthy) at template execution time
knownHelpersOnly - allows further optimizations based on the known helpers list
trackIds - include the id names used to resolve parameters for helpers
noEscape - disables HTML escaping globally
strict - templates will throw rather than silently ignore missing fields
assumeObjects - removes object existence checks when traversing paths
preventIndent - disables the auto-indententation of nested partials
stringParams - resolves a parameter to it's name if the value isn't present in the context stack
These handlebars features are currently NOT implemented:
raw block content is not passed as a parameter to helper
blockHelperMissing - helper called when a helper can not be directly resolved
helperMissing - helper called when a potential helper expression was not found
@contextPath - value set in trackIds mode that records the lookup path for the current context
@level - log level
Handlebars Lexer
You should not use the lexer directly, but for your information here is an example:
package main
import (
"fmt""github.com/flowchartsman/handlebars/v3/lexer"
)
funcmain() {
source:="You know {{nothing}} John Snow"output:=""lex:=lexer.Scan(source)
for {
// consume next tokentoken:=lex.NextToken()
output+=fmt.Sprintf(" %s", token)
// stops when all tokens have been consumed, or on erroriftoken.Kind==lexer.TokenEOF||token.Kind==lexer.TokenError {
break
}
}
fmt.Print(output)
}
Outputs:
Content{"You know "} Open{"{{"} ID{"nothing"} Close{"}}"} Content{" John Snow"} EOF
Handlebars Parser
You should not use the parser directly, but for your information here is an example:
html2text Converts HTML into text of the markdown-flavored variety Introduction Ensure your emails are readable by all! Turns HTML into raw text, usef
Dec 28, 2022
Comments
Q: How to use external hbs file
I have an external handlebar file to use on an email.
I am new to go so sorry.
All i did was:
tpl, err := handlebars.Parse("../emails/order_email.hbs")
if err != nil {
log.Fatal("Html parser error", err)
}
orderHTML, err := tpl.Exec(data) // the data to use to hbs
if err != nil {
panic(err)
}
Thanks, also i am using helpers, hoping it is correct.
On the email i see: ../emails/order_email.hbs
Bracket in identifier name causing panic in #if helper
As mentioned in https://github.com/aymerick/raymond/issues/34, the following will panic:
{{#if wat[0].fnord}}wat{{/if}}
But so will this:
{{#if wat[0]}}wat{{/if}}
I believe the original issue is something of a red herring, since you're not supposed to access array elements like this in handlebars. The correct notation would be:
{{#if wat.[0]}}wat{{/if}}
Which does NOT panic if wat does not exist.
So the question isn't so much why a missing reference is panicking, it's why the bracket is being allowed (or not) when it should be (or not), thus causing a panic.