fanbarsUser Guide
Fanbars
"Semi" logic-less templates for Fantom inspired by Mustache.
Fanbars can be used for HTML, config files, source code - anything. It works by expanding tags in a template using values provided in a Map
.
{{!-- Show the current cart contents --}} {{#if isLoggedIn}} Hello, {{username}}! <ul> {{#each item in cart}} <li>{{item.name}} ${{item.price}}</li> {{/each}} </ul> {{/if}}
We call it "semi" logic-less because there are some basic control structures for if/else
and each
blocks. Technically Mustache has these as well, but its a bit opaque (and confusing) how they get exposed. So Fanbars goes ahead and makes them explicit. This seemed like a good middle ground for what you need in real projects without adding any "real" logic to templates.
Installation
Install into your current Fantom repo using fanr
:
$ fanr install fanbars
Usage
Render text by compiling your template then renderering:
Fanbars.compile(template).render(out, map)
The input template can be an InStream
, Str
or File
instance:
Fanbars.compile(in) Fanbars.compile(file) Fanbars.compile("Hello {{name}}!")
Output can be streamed to an OutStream
instance, or return a fully rendered Str
:
f := Fanbars.compile(template) s := f.renderStr(map)// return as Strf.render(out, map)// stream to out
Syntax
Variables
Variable substitution uses the {{var}}
syntax:
template: Hello {{name}}! map: ["name":"Bob"] output: Hello Bob!
By default all variable text is HTML escaped using OutStream.writeXml. To replace text unescaped use the "triple-stash" {{{
:
template: This is {{foo}} and this is {{{foo}}} map: ["foo":"A&W"] output: This is A&W and this is A&W
Method chaining is supported for Obj variables:
template: The last day of the month is {{date.lastOfMonth.toLocale}} map: ["data":Date.today] output: The last day of the month is 31-Jan-2022
If Blocks
If blocks use the {{#if var}}
syntax, where the value of var
is considered false
if its null
or false
, everything else is true
:
template: {{#if isLoggedIn}} Hello {{name}}! {{/if}} map: ["isLoggedIn":true, "name":"Bob"] output: Hello Bob!
Use #ifnot
to check the inverse of var
:
template: {{#ifnot isLoggedIn}} Not logged in! {{/ifnot}} map: ["isLoggedIn":false] output: Not logged in!
The {{#if}}
block supports an optional is
or isnot
argument that can be used to perform a string comparison during evaluation:
template: {{#if alpha is "active"}}Alpha is active!{{/if}} {{#if beta isnot "active"}}Sorry, Beta is not currently active.{{/ifnot}} map: ["alpha":"active", "beta":"inactive"] output: Alpha is active! Sorry, Beta is not currently active.
Elvis Operator
The elivis operator ?:
can be used to inline an if-else
statement, where if the value is null
then the supplied literal is placed instead:
template: Case A: {{foo ?: "(none)"}} Case B: {{bar ?: "(none)"}} map: ["foo":125] output: Case A: 125 Case B: (none)
Each Blocks
Each blocks use the {{#each v in var}}
syntax, where the value of v
is replaced with each element in var
:
template: {{#each v in items}} Item #{{v}} {{/if}} map: ["items":[1,2,3]] output: Item #1 Item #2 Item #3
Generators
Generators use the {{#gen var}}
syntax, where var
is a Fantom function that dynamically generates content when the template is rendered at runtime:
template: {{#gen foo}} map: ["foo": |OutStream out| { out.print("generated") }] output: generated
Helpers
Helpers invoke a Fantom method to produce content based on one or more vars:
template: {{#helper acme::Bar.helper foo}} map: ["foo":"Bob"] acme::Bar.helper: Str helper(Obj? val) { "Hello there ${val}!" } output: Hello there Bob!
Helper funcs may take multiple arguments, where each arg may be a var
def or a string literal:
template: {{#helper acme::Bar.helper foo bar "some string"}}
Partials
Partials use the {{#partial var}}
syntax to inject content from another template. The partial is rendered at runtime. Partials must be compiled ahead of time and passed to render()
:
Fantom: p := ["welcome": Fanbars.compile(welcome)] s := Fanbars.compile(template).renderStr(map, p) welcome: Welcome, {{user}}! template: {{#partial welcome}} Enjoy your stay. map: ["user": "Andy"] output: Welcome, Andy! Enjoy your stay
Comments
Comments use the {{!-- text --}}
sytnax. Comments are omitted from the rendered output:
template: {{!-- this is a comment --}} Hello, World output: Hello, World