A simple html render libary more than Mustache Template Complier
Mustache is a framework-agnostic templating system that enforces separation of view logic from the template file. Indeed, it is not even possible to embed logic in the template. This allows templates to be reused across language boundaries and for other language independent uses.
Working with Mustache means dealing with templates, views, and contexts. Templates contain HTML (or some other format) and Mustache tags that specify what data to pull in. A template can be either a string or a file (usually ending in .mustache). Views are Erlang modules that can define functions that are called and provide the data for the template tags. A context is an Erlang dict that contains the current context from which tags can pull data. A few examples will clarify how these items interact.
aihtml uses erlang.mk as its building tool. So currently, it only support erlang.mk.
The target of aihtml is to help user to build a simple view engine in the Erlang. And aihtml uses a modified version muatche compiler from bbmustache. But there are some difference between bbmustache.
bbmustahce:
- It supports the standards mustache sytanx.
- Very light, it won't create any process or ets.
- It can compile mustache file or render directly.
- It can render mustache string directly.
aihtml:
- It also supports the standards mustache sytanx.
- It adds lamda section on mustache sytanx.
- Very heavy, it will create a process and use an ets to store some information.
- It must compile mustache file before rendering, and store the compile result in the ets for resusing.
- It can't render mustache string directly.
In v0.3.5 we start using the atom key to replace the binary key of tags.
So when using v0.3.5 or above, please use atom keys in context
to render the templates.
aihtml has to bootstrap before rendering mustache files.
It boostraps using function ai_mustache:bootstrap
, it will using views
directory as default directory where the mustache files are stored. And will compile all mustache files with the suffix .mustache
into IR code and store them in ets.
bootstrap()-> ai_mustache_loader:bootstrap().
And there is a function which can accept one params settings to change default settings.
bootstrap(Settings) -> ai_mustache_loader:bootstrap(Settings).
Settings :: #{
views := binary(),
suffix := binary()
}.
aihtml only support maps
as context params when it render a mustache file. And the key must be a binary
.
#{
<<"user">> => #{
<<"name">> => "David Gao",
<<"level">> => 1
},
<<"stars">> => 10
}
aihtml supports partials, and it will auto load the partial mustache file from views
directory which can be modified by bootstrap.
{{> shared/user }}
If we use the default settings, aihtml will load user.mustache
from views/shared
directory auto.
Section in context is a list
friends.mustache
<ul>
{{# friends }}
<li>
<img src="{{{ friends.avatar }}}"/>
<span>{{ friends.name }}</span>
</li>
{{/ friends }}
</ul>
friends_context.erl
Context = #{
<<"friends">> => [
#{<<"name">> => "Jane", <<"avatar">> => "/images/avatar/ jane.png" },
#{<<"name">> => "David", <<"avatar">> => "/images/avatar/ David.png" },
]
}
Section in context is fun/2
The first param of function will be the rendered binary inside the section.
friends.mustache
{{# warpped }}
<ul>
{{# friends }}
<li>
<img src="{{{ friends.avatar }}}"/>
<span>{{ friends.name }}</span>
</li>
{{/ friends }}
</ul>
{{/ warpped}}
friends_context.erl
warpped(Acc,Context) -> <<"<div> ",Acc/binary," </div>" >>.
Context = #{
<<"friends">> => [
#{<<"name">> => "Jane", <<"avatar">> => "/images/avatar/ jane.png" },
#{<<"name">> => "David", <<"avatar">> => "/images/avatar/ David.png" },
],
<<"warpped">> => fun warpped/2
}
Inverted section in context is a list
or not exsist
friends.mustache
{{^ friends }}
<div> Want to know some new friends ? </div>
{{/ friends }}
friends_context.erl
Context = #{
<<"friends">> => []
}
or
Context = #{}
Has section in context is map
navbar.mustache
{{+ user }}
<div>
<span>{{user.name}}</span>
<span>{{user.level}}</span>
</div>
{{/ user }}
navbar_context.erl
#{
<<"user">> => #{
<<"name">> => "David Gao",
<<"level">> => 1
}
}
Has section in context is bool
or binary
friends.mustache
{{+ has_friends }}
<ul>
{{# friends }}
<li>
<img src="{{{ friends.avatar }}}"/>
<span>{{ friends.name }}</span>
</li>
{{/ friends }}
</ul>
{{/ has_friends}}
friends_context.erl
Context = #{
<<"friends">> => [
#{<<"name">> => "Jane", <<"avatar">> => "/images/avatar/ jane.png" },
#{<<"name">> => "David", <<"avatar">> => "/images/avatar/ David.png" },
],
<<"has_friends">> => true
}
Has section in context is fun/1
friends.mustache
{{+ has_friends }}
<ul>
{{# friends }}
<li>
<img src="{{{ friends.avatar }}}"/>
<span>{{ friends.name }}</span>
</li>
{{/ friends }}
</ul>
{{/ has_friends}}
friends_context.erl
has_friends(Context) ->
case maps:get(<<"friends>>,Context, undefined) of
undefined -> false;
_ -> true
end.
Context = #{
<<"friends">> => [
#{<<"name">> => "Jane", <<"avatar">> => "/images/avatar/ jane.png" },
#{<<"name">> => "David", <<"avatar">> => "/images/avatar/ David.png" },
],
<<"has_friends">> => fun has_friends/1
}
Inverted has section in context is bool
friends.mustache
{+ has_friends }}
<ul>
{{# friends }}
<li>
<img src="{{{ friends.avatar }}}"/>
<span>{{ friends.name }}</span>
</li>
{{/ friends }}
</ul>
{{/ has_friends}}
{{- has_friends }}
<div> Want to know some new friends ? </div>
{{/ has_friends }}
friends_context.erl
Context = #{
<<"friends">> => [
#{<<"name">> => "Jane", <<"avatar">> => "/images/avatar/ jane.png" },
#{<<"name">> => "David", <<"avatar">> => "/images/avatar/ David.png" },
],
<<"has_friends">> => true
}
Inverted has section in context is fun/1
friends.mustache
{{+ has_friends }}
<ul>
{{# friends }}
<li>
<img src="{{{ friends.avatar }}}"/>
<span>{{ friends.name }}</span>
</li>
{{/ friends }}
</ul>
{{/ has_friends}}
{{- has_friends }}
<div> Want to know some new friends ? </div>
{{/ has_friends }}
friends_context.erl
has_friends(Context) ->
case maps:get(<<"friends>>,Context, undefined) of
undefined -> false;
_ -> true
end.
Context = #{
<<"friends">> => [
#{<<"name">> => "Jane", <<"avatar">> => "/images/avatar/ jane.png" },
#{<<"name">> => "David", <<"avatar">> => "/images/avatar/ David.png" },
],
<<"has_friends">> => fun has_friends/1
}
This is an extends of aihtml on mustach syntax.
Lambda in context is fun/1
layout.mustache
{{* yield}}
layout_context.erl
yield(Context) -> ......
Context = #{
<<"yield">> => fun yield/1
}
Lambda in context is fun/2
and a value
layout.mustache
{{* yield}}
layout_context.erl
yield(Template,Context)->
ai_mustache:render(Template,Context).
render(Template,State) ->
Context = maps:get(context,State,#{}),
Layout = maps:get(layout,State,<<"layout/default">>),
LayoutContext = Context#{ <<"yield">> => [fun yield/2,Template] },
ai_mustache:render(Layout,LayoutContext).
- aiwiki a very simple blog.