The Haxe Template System
Haxe comes up with a standard Template System with an easy to use syntax which is interpreted by a lightweight class called haxe.Template.
A Template is a string or a file that is used to produce some HTML or XML output depending on the input. Here's a small template example :
My name is <strong>::name::</strong> and I'm <em>::age::</em> years old.
Let's save this in a file named sample.mtt
. You can use it from Haxe by using the following code :
class App { static function main() { var str = haxe.Resource.getString("my_sample"); var t = new haxe.Template(str); var output = t.execute({ name : "John", age : 33 }); trace(output); } }
You can compile this example, and include the sample.mtt
by using the following HXML :
# enable the platform you want by removing the sharp : #-swf app.swf #-neko app.n #-js app.js -resource sample.mtt@my_sample -main App
This should display the string where the variables have been replaced by the values passed at runtime in the context.
Expressions
Between the ::
, you can put some expression. The syntax is currently restricted to the current possibilities :
::name::
: the variablename
::(135)::
: the integer 135. Float constants are not allowed::(expr)::
: the expressionexpr
is evaluated::(e1 op e2)::
: the operationop
is applied toe1
ande2
::expr.field::
: field access
The syntax is a bit restrictive, since it doesn't deal with operator precedence, so you need to put parenthesis around each operator : for example ::((1 + 3) == (2 + 2))::
will display true
. There are no calls either.
If/Else/Elseif
You can add some tests in the template :
::if flag:: OK ! ::end:: ::if flag:: OK ! ::else:: FAILED ! ::end:: ::if flag1:: OK ! ::elseif flag2:: MAYBE ::else:: NO ::end::
Foreach
You can iter on a structure (an Iterator or an Iterable Data structure) by using foreach
:
<table> <tr><td>Name :</td><td>Age :</td></tr> ::foreach users::<tr><td>::name::</td><td>::age::</td></tr>::end:: </table>
You can access the current context value of the foreach using the special variable __current__, especially useful when iterating over nested containers:
::foreach rows:: ::foreach __current__:: ::end:: ::end::
A Complete Sample
Here's a Template using several aspects of the syntax :
The habitants of <em>::name::</em> are : <ul> ::foreach users:: <li> ::name:: ::if (age > 18)::Grown-up::elseif (age <= 2)::Baby::else::Young::end:: </li> ::end:: </ul>
And here's the class filling the Data :
class User { var name : String; var age : Int; public function new(name,age) { this.name = name; this.age = age; } } class Town { var name : String; var users : Array<User>; public function new( name ) { this.name = name; users = new Array(); } public function addUser(u) { users.push(u); } } class App { static function main() { // build the data model var town = new Town("Paris"); town.addUser( new User("Marcel",88) ); town.addUser( new User("Julie",15) ); town.addUser( new User("Akambo",2) ); // display it var str = haxe.Resource.getString("my_sample"); var t = new haxe.Template(str); var output = t.execute(town); // if on the server, don't add file/line infos #if neko neko.Lib.print(output); #else true trace(output); #end } }
When running the template system on the server side, you can simply use neko.Lib.print
instead of trace
to display the HTML template to the user.
Sub-templates
Often users want to be able to include templates inside other templates. To do that, simply pass the sub-template result string as a parameter :
var t1 = new haxe.Template("My sub template is ::sub::"); var t2 = new haxe.Template("::foreach items:: (::i::) ::end::"); var t2str = t2.execute({ items : [{ i : 0 },{ i : 33 },{ i : -5 }] }); var t1str = t1.execute({ sub : t2str }); trace(t1str);
Globals
You can use the Template.globals object to store those values that should be applied to all templates.
Macros
If you want to be able to callback your code when some parts of the template are rendered, you can use Macros for that. Here's a small example :
class App { // a macro returns a string static function myfun( resolve : String -> Dynamic, title : String, p : Int ) { return "["+title+"="+(p * resolve("mult"))+"]"; } static function main() { var t1 = new haxe.Template("Call macro : $$myfun(Hello,::param::)"); var str = t1.execute({ param : 55, mult : 2 },{ myfun : myfun }); trace(str); //output: "Call macro : [Hello=110]" } }
A macro is called with the following parameters :
- the
resolve
function, which can be called to retreive some value from the Template context. - all the parameters passed in the Template. If this parameter is a context variable
::v::
(without spaces around !) then it is passed to the macro without any string conversion. If it's a more complex expression, it's evaluated first, then passed to the macro.