Mario
Continues work of raymond to support Handlebars for golang
Handlebars
Handlebars is a superset of mustache but it differs on those points:
- Alternative delimiters are not supported
- There is no recursive lookup
Install
go get -u github.com/imantung/mario
Usage
Mario's function implementation mimic go template for convenience.
source := `Hello {{name}}`
ctx := map[string]string{
"name": "World",
}
tpl, err := mario.New().Parse(source)
if err != nil {
panic(err)
}
var b strings.Builder
if err := tpl.Execute(&b, ctx); err != nil {
panic(err)
}
fmt.Println(b.String())
// Output:
// Hello World
For shortcut, using Must
to create template object without error
var t = mario.Must(mario.New().Parse(source))
Rendering Context
The rendering context can contain any type of values, including array
, slice
, map
, struct
and func
.
Map
source := `{ "name": "{{name}}", "age": {{age}} }`
data := map[string]interface{}{
"name": "Mario",
"age": 12,
}
Struct
source := `<div class="post">
<h1>By {{author.firstName}} {{author.lastName}}</h1>
<div class="body">{{body}}</div>
<h1>Comments</h1>
{{#each comments}}
<h2>By {{author.firstName}} {{author.lastName}}</h2>
<div class="body">{{content}}</div>
{{/each}}
</div>`
ctx := Post{
Person{"Jean", "Valjean"},
"Life is difficult",
[]Comment{
Comment{
Person{"Marcel", "Beliveau"},
"LOL!",
},
},
}
Note:
-
When using structs, be warned that only exported fields are accessible.
type Person struct { FirstName string LastName string motherName string // NOTE: Unexported and can't be access }
-
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 asAuthor
andFirstName
are exported struct fields. -
More, you can use the
handlebars
struct tag to specify a template variable name different from the struct field name.
Function
Randomly renders I hate you
or I love you
.
source := "I {{feeling}} you"
ctx := map[string]interface{}{
"feeling": func() string {
rand.Seed(time.Now().UTC().UnixNano())
feelings := []string{"hate", "love"}
return feelings[rand.Intn(len(feelings))]
},
}
Note:
- Those context functions behave like helper functions: they can be called with parameters and they can have an
Options
argument.
Helpers
Handlebarjs built-in helpers:
-
if
to conditionally render a block.<div class="entry"> {{#if author}} <h1>{{firstName}} {{lastName}}</h1> {{/if}} </div>
-
unless
inverse of theif
helper.<div class="entry"> {{#unless license}} <h3 class="warning">WARNING: This entry does not have a license!</h3> {{/unless}} </div>
-
each
: iterate over an array, a slice, a map or a struct instance using this built-ineach
helper. Inside the block, you can usethis
to reference the element being iterated over.<ul class="people"> {{#each people}} <li>{{this}}</li> {{/each}} </ul>
-
with
: shift the context for a section of a template by using the built-inwith
block helper.<div class="entry"> <h1>{{title}}</h1> {{#with author}} <h2>By {{firstName}} {{lastName}}</h2> {{/with}} </div>
-
lookup
: allows for dynamic parameter resolution using handlebars variables.{{#each bar}} {{lookup ../foo @index}} {{/each}}
-
log
: allows for logging while rendering a template.{{log "Look at me!"}}
Additional helper:
-
equal
: renders a block if the string version of both arguments are equals.{{#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}}
Custom Helper
TODO: Implementation of custom helper
Language Features
TODO: https://handlebarsjs.com/guide/#language-features
Limitations
These handlebars options are currently NOT implemented:
compat
- enables recursive field lookupknownHelpers
- list of helpers that are known to exist (truthy) at template execution timeknownHelpersOnly
- allows further optimizations based on the known helpers listtrackIds
- include the id names used to resolve parameters for helpersnoEscape
- disables HTML escaping globallystrict
- templates will throw rather than silently ignore missing fieldsassumeObjects
- removes object existence checks when traversing pathspreventIndent
- disables the auto-indententation of nested partialsstringParams
- 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 resolvedhelperMissing
- helper called when a potential helper expression was not found@contextPath
- value set intrackIds
mode that records the lookup path for the current context@level
- log level
References
- http://handlebarsjs.com/
- https://mustache.github.io/mustache.5.html
- https://github.com/golang/go/tree/master/src/text/template
- https://www.youtube.com/watch?v=HxaD_trXwRE
- https://github.com/aymerick/raymond
Others Implementations
- raymond - golang (OG)
- handlebars.js - javascript
- handlebars.java - java
- handlebars.rb - ruby
- handlebars.php - php
- handlebars-objc - Objective C
- rumblebars - rust