haXe 2: Using Templates

0
81
9 min read

 

haXe 2 Beginner’s Guide

haXe 2 Beginner's Guide

Develop exciting applications with this multi-platform programming language

        Read more about this book      

(For more resources on this subject, see here.)

Introduction to the haxe.Template class

As developers our job is to create programs that allow the manipulation of data. That’s the basis of our job, but beyond this part of the job, we must also be able to present that data to the user. Programs that don’t have a user interface exist, but since you are reading this article about haXe, there is a greater chance that you are mostly interested in web applications, and almost all web applications have a User Interface of some kind. However, these can also be used to create XML documents for example.

The haXe library comes with the haxe.Template class. This class allows for basic, yet quite powerful, templating: as we will see, it is not only possible to pass some data to it, but also possible to call some code from a template.

Templates are particularly useful when you have to present data—in fact, you can, for example, define a template to display data about a user and then iterate over a list of users displaying this template for each one.

We will see how this is possible during this article and we will see what else you can do with templates. We will also see that it is possible to change what is displayed depending on the data and also that it is easy to do some quite common things such as having a different style for one row out of two in a table.

The haxe.Template is really easy to use—you just have to create an instance of it passing it a String that contains your template’s code as a parameter. Then it is as easy as calling the execute method and giving it some data to display.

Let’s see a simple example:

class TestTemplate
{
public static function main(): Void
{
var myTemplate = new haxe.Template("Hi. ::user::");
neko.Lib.println(myTemplate.execute({user : "Benjamin"}));
}
}

This simple code will output “Hi. Benjamin”. This is because we have passed an anonymous object as a context with a “user” property that has “Benjamin” as value.

Obviously, you can pass objects with several properties. Moreover, as we will see it is even possible to pass complex structures and use them.

In addition, we certainly won’t be hard coding our templates into our haXe code. Most of the time, you will want to load them from a resource compiled into your executable by calling haxe.Resource.getString or by directly loading them from the filesystem or from a database.

Printing a value

As we’ve seen in the preceding sample, we have to surround an expression with :: in order to print its value.

Expressions can be of several forms:

Form

Explanation

::variableName::

The value of the variable.

::(123)::

The integer 123. Note that only integers are allowed.

::e1 operator e2::

Applies the operator to e1 and e2 and returns the resulting value.

The syntax doesn’t manage operator precedence, so you should wrap expressions inside parenthesis.

::e.field::

Accesses the field and returns the value.

Be warned that this doesn’t work with properties’ getters and setters as these properties are a compile-time only feature.

Branching

The syntax offers the if, else, and elseif:

class TestTemplate
{
public static function main(): Void
{
var templateCode = "::if (sex==0):: Male ::elseif (sex==1)::
Female ::else:: Unknown ::end::";
var myTemplate = new haxe.Template(templateCode);
neko.Lib.print(myTemplate.execute({user : "Benjamin", sex:0}));
}
}

Here the output will be Male. But if the sex property of the context was set to 1 it would print Female, if it is something else, it will print “Unknown”.

Note that our keywords are surrounded by :: (so the interpreter won’t think that it is just some raw-text to be printed).

Also note that the “end” keyword has been introduced since we do not use braces.

Using lists, arrays, and other iterables

The template engine allows one to iterate over an iterable and repeat a part of the template for each object in the iterable.

This is done using the ::foreach:: keyword. When iterating, the context will be modified and will become the object that is actually selected in the iterable.

It is also possible to access this object (indeed, the context’s value) by using the __current__ variable.

Let’s see an example:

class Main
{
public static function main()
{
//Let's create two departments:
var itDep = new Department("Information Technologies Dept.");
var financeDep = new Department("Finance Dept.");

//Create some users and add them to their department
var it1 = new Person();
it1.lastName = "Par";
it1.firstName = "John";
it1.age = 22;

var it2 = new Person();
it2.lastName = "Bear";
it2.firstName = "Caroline";
it2.age = 40;

itDep.workers.add(it1);
itDep.workers.add(it2);

var fin1 = new Person();
fin1.lastName = "Ha";
fin1.firstName = "Trevis";
fin1.age = 43;

var fin2 = new Person();
fin2.lastName = "Camille";
fin2.firstName = "Unprobable";
fin2.age = 70;

financeDep.workers.add(fin1);
financeDep.workers.add(fin2);

//Put our departements inside a List:
var depts = new List<Department>();
depts.add(itDep);
depts.add(financeDep);

//Load our template from Resource:
var templateCode = haxe.Resource.getString("DeptsList");
//Execute it
var template = new haxe.Template(templateCode);
neko.Lib.print(template.execute({depts: depts}));
}
}

class Person
{
public var lastName : String;
public var firstName : String;
public var age : Int;

public function new()
{

}
}

class Department
{
public var name : String;
public var workers : List<Person>;

public function new(name : String)
{
workers = new List<Person>();
this.name = name;
}
}

In this part of the code we are simply creating two departments, some persons, and adding those persons into those departments.

Now, we want to display the list of departments and all of the employees that work in them. So, let’s write a simple template (you can save this file as DeptsList.template):

<html>
<head>
<title>Workers</title> </head>
<body>
::foreach depts::
<h1>::name::</h1>
<table>
::foreach workers::
<tr>
<td>::firstName::</td>
<td>::lastName::</td>
<td>::if (age < 35)::Junior::elseif (58):
:Senior::else::Retired::end::</td>
</tr>
::end::
</table>
::end::
</body>
</html>

When compiling your code you should add the following directive:

-resource DeptsList.template@DeptsList

The following is the output you will get:

<html>
<head>
<title>Workers</title> </head>
<body>

<h1>Information Technologies Dept.</h1>
<table>

<tr>
<td>John</td>
<td>Par</td>
<td>Junior</td>
</tr>

<tr>
<td>Caroline</td>
<td>Bear</td>
<td>F</td>
</tr>

</table>

<h1>Finance Dept.</h1>
<table>
<tr>
<td>Trevis</td>
<td>Ha</td>
<td>Senior</td>
</tr>

<tr>
<td>Unprobable</td>
<td>Camille</td>
<td>Retired</td>
</tr>

</table>

</body>
</html>

As you can see, this is indeed pretty simple once you have your data structure in place.

Time for action – Executing code from a template

Even though templates can’t contain haXe code, they can make calls to so-called “template macros”. Macros are defined by the developer and, just like data they are passed to the template.execute function. In fact, they are passed exactly in the same way, but as the second parameter.

Calling them is quite easy, instead of surrounding them with :: we will simply prefix them with $$, we can also pass them as parameters inside parenthesis. So, let’s take our preceding sample and add a macro to display the number of workers in a department.

First, let’s add the function to our Main class:

public static function displayNumberOfWorkers(resolve :
String->Dynamic, department : Department)
{
return department.workers.length + " workers";
}

Note that the first argument that the macro will receive is a function that takes a String and returns a Dynamic. This function will allow you to retrieve the value of an expression in the context from which the macro has been called.

Then, other parameters are simply the parameters that the template passes to the macro. So, let’s add a call to our macro:

<html>
<head>
</head>
<body>
::foreach depts::
<h1>::name:: ($$displayNumberOfWorkers(::__current__::))</h1>
<table>
::foreach workers::
<tr>
<td>::firstName::</td>
<td>::lastName::</td>
<td>::if (sex==0)::M::elseif (sex==1)::F::else::?::end::</td>
</tr>
::end::
</table>
::end::
</body>
</html>

As you can see, we will pass the current department to the macro when calling it to display the number of workers.

So, here is what you get:

<html>
<head>
</head>
<body>

<h1>Information Technologies Dept. (2 workers)</h1>
<table>

<tr>
<td>John</td>
<td>Par</td>
<td>M</td>
</tr>

<tr>
<td>Caroline</td>
<td>Bear</td>
<td>F</td>
</tr>

</table>

<h1>Finance Dept. (2 workers)</h1>
<table>

<tr>
<td>Trevis</td>
<td>Ha</td>
<td>M</td>
</tr>

<tr>
<td>Unprobable</td>
<td>Camille</td>
<td>?</td>
</tr>

</table>

</body>
</html>

What just happened?

We have written the displayNumberOfWorkers macro and added a call to it in the template. As a result, we’ve been able to display the number of workers in a department.

Integrating subtemplates

Sub-templates do not exist as such in the templating system.

The fact is that you can include sub-templates into a main template, which is not a rare process. Some frameworks, not only in haXe, have even made this standard behavior.

So, there are two ways of doing this:

  • Execute the sub-template, store its return value, and pass it as a property to the main template when executing it.
  • Create a macro to execute the sub-template and return its value. This way you just have to call the macro whenever you want to include your sub-template in your main template.

Creating a blog’s front page

In this section, we are going to create a front page for a blog by using the haxe.Template class.

We will also use the SPOD system to retrieve posts from the database.

LEAVE A REPLY

Please enter your comment!
Please enter your name here