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 = Std.resource("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 the current possibilities :
::name::: the variablename::(135)::: the integer 135. Float constants are not allowed::(expr)::: the expressionexpris evaluated::(e1 op e2)::: the operationopis applied toe1ande2::expr.field::: field access
The syntax is a bit restrictive, since it doesn't deal with operator precedence, you need to put parenthesis around each operator : for example ::((1 + 3) == (2 + 2)):: will display true. There is 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>
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 : List<User>; public function new( name ) { this.name = name; users = new List(); } public function addUser(u) { users.add(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 = Std.resource("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);
Macros
If you want to be able to callback your code when some part 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); } }
A macro is called with the following parameters :
- the
resolvefunction, 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.