Introduction
Ader TemplateEngine is a .NET class library (written in C#) for generating text output from source template and input parameters. It can be used in many scenarios: website page building, email generation, XML generation, source code generation, etc. Its idea is based on Antlr stringTemplate
, but the syntax is based on ColdFusion language.
Currently, .NET Ader TemplateEngine works with .NET 2.0 only, but I have plans on making it work with .NET 1.0/1.1 and maybe Java. Ader TemplateEngine is released under GNU General Public License.
Here is a very simple template:
Thank You for your order #order.billFirstName# #order.billLastName#.
<br>
Your Order Total is: #format(order.total, "C")#
The templates can have expressions, if
/elseif
/else
statements, foreach
statement and other templates.
Templates API:
There are two classes mainly used in Template Engine: Template
and TemplateManager
. Template
holds a single instance of a template and TemplateManager
is used for executing templates. Easiest way of creating templates is by using static methods of Template
or TemplateManager
:
Template template = Template.FromString(string name, string data);
Template template = Template.FromFile(string name, string filename);
Then you use it to instantiate TemplateManager
:
TemplateManager mngr = new TemplateManager(template);
or even easier:
TemplateManager mngr = TemplateManager.FromFile(filename);
TemplateManager mngr = TemplateManager.FromString(template);
When using FromString
method, the string passed contains template code. This method can be used to dynamically generate text without having templates in files. You use SetValue(string name, object value);
to add values that can be used within the templates.
E.g.:
mngr.SetValue("customer", new Customer("Tom", "Jackson"));
Then you can refer to customer
within the template. You can use any type of object for value. When the value of variable is to be output, ToString()
method will be called.
Expressions
Expressions are enclosed with #
(hash or pound) characters:
Example:
#firstName#
This example will output value of first name. If you need to output #
character, just escape it with another #
.
Example:
Your SS## is #ssnumber#
Inside of expression block, you can output any variable:
#somevar#
Access property of a variable:
#somestring.Length#
Property name is not case sensitive. So you can call: #string.length#
or #string.LENGTH#
. Or call a function:
#trim(somename)#
There are several built in functions, and additional functions can be easily added. The built in functions are:
equals(obj1, obj2)
- invokes equals
method on obj1
with obj2
as parameter. Returns boolean value.
notequals(obj1, obj2)
- Returns !equals(obj1, obj2)
. Is equivalent to calling: not(equals(obj1, obj2))
.
iseven(num)
- tests whether number is an even number.
isodd(num)
- tests whether number is an odd number.
isempty(string)
- tests whether string has 0 characters. Same as equals(string.Length, 0)
.
isnotempty(string)
- tests whether string has at least 1 character.
isnumber(num)
- tests whether num
is of numeric type.
toupper(string)
- converts string
to upper case.
tolower(string)
- converts string
to lower case.
isdefined(varname)
- tests whether variable named varname
is defined.
ifdefined(varname, value)
- returns value if varname
is defined. Especially useful: #ifdefined("name", name)#
- will output value of name
if it's defined, otherwise will output nothing.
len(string)
- returns length of string
.
tolist(collection, property, delim)
- will convert collection
to string with delim
as separator. If you pass property
, the value of the property
will be evaluated on each element of the collection. If you omit property
, then the object itself will be used.
Example: suppose you have list as:
ArrayList list = new ArrayList();
list.Add("one");
list.Add("two");
list.Add("three");
template.SetValue("mylist", list);
Then in your template:
#toList(mylist, " & ")#
the output will be: one & two & three
Suppose you have list as:
list.Add(new Customer("Tom", "Whatever"));
list.Add(new Customer("Henry", "III"));
list.Add(new Customer("Tom", "Jackson"));
template.SetValue("mylist", list);
Then in template:
#toList(mylist, "firstName", ",")#
The output will be: Tom,Henry,Tom.
isnull(obj)
- tests whether obj
is null
.
not(boolvalue)
- returns not (!) of boolean value.
iif(booleanExpression, iftruevalue, iffalsevalue)
- same as booleanExpression ? iftruevalue : iffalsevalue
in C#.
Example:
#iif(isodd(i), "bgcolor=yellow", "bgcolor=red")#
will output bgcolor=yellow if i
is odd number, and bgcolor=red if i
is not odd number.
format(object, formatstring)
- will call ToString(formatstring)
on object
. object
has to implement IFormattable
interface, otherwise ToString()
will be called.
Example: suppose total
is decimal with value 1208.45:
#format(total, "C")#
will output: $1,208.45
trim(string)
- will trim string
object
filter(collection, booleanproperty)
- will return new List from collection
for those objects whose boolean property property evaluates to true
.
gt(obj1, obj2)
- will return true
if obj1
> obj2
. (obj1
and obj2
must implement IComparable
. All numeric types do).
lt(obj1, obj2)
- will return true
if obj1
< obj2
. (obj1
and obj2
must implement IComparable
. All numeric types do).
compare(obj1, obj2)
- will return -1 if obj1
< obj2
, 0 if obj1
= obj2
, and 1 if obj1
> obj2
. (obj1
and obj2
must implement IComparable
. All numeric types do).
or(bool1, bool2)
- will return true
if either bool1
or bool2
are true
.
Example:
#or(equals(state, "IL"), equals(state, "NY"))# -
returns true if state is either IL or NY
and(bool1, bool2)
- will return true
if both bool1
and bool2
are true
.
comparenocase(string1, string2)
- will do case insensitive comparison of string1
and string2
and return true
if they are equal.
stripnewlines(string)
- will return all \r\n
instances and replace them with space.
Built In Tags:
IF
You can also conditionally output text based on some expression using special if
tag:
<ad:if test="#booleanexpression#">
<ad:elseif test="#bool#">
<ad:else>
</ad:if>
elseif
and else
are optional. If test of "if
" evaluates to true
, then block inside of "if
" will be output, otherwise elseif
will be tested (if exists) and then else
.
Example:
<ad:if test="#equals(cust.country, "US"))#">
You are US customer.
<ad:else>
You are from: #cust.country# country.
</ad:if>
If cust.country
is "US" then the output will be: You are US customer.
FOREACH
You can loop through collection of elements (any object that implements IEnumerable
interface) using FOREACH
tag.
<ad:foreach collection="#collection#" var="cust" index="i">
#i#: #cust.lastname#, #cust.firstname#
</ad:foreach>
Suppose customers
is array of customer objects:
customers = Customer("Tom", "Jackson")
Customer("Mary", "Foo")
The output will be:
- Jackson, Tom
- Foo, Mary
During execution, variable name that is passed as var
attribute will be assigned with element from the collection. index
attribute can be omitted, and is used to represent index variable for the loop. It starts with 1 and gets incremented with each iteration.
Custom Templates:
You can also create your own templates inside of template file, that you can call. You do that using template
tag:
<ad:template name="ShowCustomer">
#customer.lastname#, #customer.firstname#
</ad:template>
<ad:showcustomer customer="#cust#" />
You can pass any attribute to the template, and you can use those inside of the template. The template can also access all variables that are defined outside of the template. When calling template, you have to put trailing slash at the end, or put closing tag:
<ad:showcustomer />
or
<ad:showcustomer></ad:showcustomer>
The template also receives special variable: innerText
that is the content of executing the inner elements of calling template.
<ad:template name="bold">
<b>#innerText#</b>
</ad:template>
<ad:bold>#cust.lastname#, #cust.firstname#</ad:bold>
the output will be:
<b>Jackson, Tom</b>
(if customer is Tom Jackson)
You can also nest those:
<ad:template name="italic">#innerText#</ad:template>
<ad:bold><ad:italic>This will be bold and italic</ad:italic></ad:bold>
You can also invoke templates based on the name, using apply
tag:
<ad:apply template="#usetemplate#">this is content</ad:apply>
If usetemplate
is "bold", then "bold" template will be called.
Templates can be nested inside other templates:
<ad:template name="doit">
<ad:template name="colorme">
<font color=#color#>#innerText#</font>
</ad:template>
<ad:colorme color="blue">colorize me</ad:colorme>
</ad:template>
colorme
template can only be used within doit
template. Templates can also be added programmatically:
TemplateManager mngr = ...;
mngr.AddTemplate(Template.FromString("bold", "<b>#innerText#</b>"));
Now, bold
template can be used anywhere within processing. Here is a sample based on order confirmation:
class Order
{
string firstname, lastname, address1, city, state, zip, country;
public string Address1
{
get { return this.address1; }
}
public string City
{
get { return this.city; }
}
public string Country
{
get { return this.country; }
}
public string Firstname
{
get { return this.firstname; }
}
public string Lastname
{
get { return this.lastname; }
}
public string State
{
get { return this.state; }
}
public string Zip
{
get { return this.zip; }
}
}
Order order = GetOrder();
TemplateManager mngr = TemplateManager.FromFile("order-confirmation.st");
mngr.SetValue("order", order);
System.IO.StringWriter writer = new System.IO.StringWriter();
mngr.Process(writer);
string emailBody = writer.ToString();
order-confirmation.st
<ad:showitem>
#item.sku# - #item.name#<br>
<ad:if test="#equals(item.qty, 1)#">
Price: #format(item.price, "C")#<br>
<ad:else>
You bought #item.qty# items for #format(item.price, "C")#
(total: #format(item.total, "C")#)
</ad:if>
</ad:showitem>
#order.firstname# #order.lastname#<br>
#order.address1#<br>
<ad:if test="#isnotempty(order.address2)#">#order.address2#<br></ad:if>
#order.city#, #order.zip# #order.state#
<br>
<table>
<ad:foreach collection="#order.orderitems#" var="orderitem" index="i">
<tr>
<td>#i#.</td>
<td bgcolor="#iif(isodd(i), "##DEDEDE", "white")#">
<ad:showitem item="#orderitem#" />
</td>
</tr>
</ad:foreach>
</table>
Shipping: #format(order.shipping, "C")#<br>
Taxes: #format(order.tax, "C")#<br>
Order Total: #format(order.total, "C")#<br>
Description of order-confirmation.st:
First, showitem
template is defined which shows a single line item of the order. item
is passed as attribute to showitem
. Then address is shown. Note how if
is used to conditionally display second line of address with ending <br> tag. Then each line item of order is looped through using ad:foreach
tag. iif
function is used to color every other line with #DEDEDE color.
Example #2 for constructing complex SQL queries:
string[] cols = new string[]{"id", "name", "email"};
TemplateManager mngr = TemplateManager.FromFile(file);
mngr.SetValue("colums", cols);
mngr.SetValue("tablename", "customer");
string query = mngr.Process();
And the template file is:
select #toList(columns, ",")# from #tablename#
Example 1 project has two sample templates that are used to process the same data. First, it outputs it as a C# class to the screen, then it uses HTML template to create HTML file.
If you have any questions, you can use AderTemplates forums.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.