haXe

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 variable name
  • ::(135):: : the integer 135. Float constants are not allowed
  • ::(expr):: : the expression expr is evaluated
  • ::(e1 op e2):: : the operation op is applied to e1 and e2
  • ::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 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.

version #1063, modified 2008-05-03 00:06:38 by ncannasse