Haxe模板系统

Haxe提供了一个易用的标准模板系统,模板的解析工作由轻量级的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 :

模板是一个字符串或文件,在系统处理后输出为HTML或XML等格式化文本。下面就是一个简单的模板范例:

我叫 <strong>::name::</strong> ,年龄为<em>::age::</em>岁。

将它保存为 sample.mtt 文件,然后我们用如下代码解析它:

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);
    }
}

将这段代码用以下HXML文件编译,注意,在编译文件中有一个参数-resource中将sample.mtt整合到了程序中:

# 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

运行编译后的程序,就可以看到在t.execute()函数中提供的变量值已经替换到了模板相应的字段中。

表达式

Between the ::, you can put some expression. The syntax is currently restricted to the current possibilities :

在两个"::"字符串之间插入表达式,即为模板语句,模板语句的运算处理会被放在模板的相应区域中。这些表达式的语法可以是以下形式:

  • ::name:: : 变量 name 的值。
  • ::(135):: : 整形数值135,不允许使用浮点值。
  • ::(expr):: : 表达式expr的计算结果。
  • ::(e1 op e2):: : e1和e2两个表达式的结果通过操作符 op 处理的结果。
  • ::expr.field:: : 访问表达式的字段的值

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

在模板中可以使用如下的条件判断:

::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.

version #12358, modified 2012-02-15 14:24:37 by icebirds