Client-side Templates

Description

There are a rich set of template directives that make templates both extremely powerful and also easy to use.

Overview

 What are Client-side Templates?

Client-side Templates let you to merge complex JavaScript data in richly formatted templates to produce HTML that can be displayed in a UX or Grid component.

The basic idea of Client-side Templates is that you have some JavaScript data - typically a JavaScript object, or an array of JavaScript objects, and a template (with placeholders for data) and you merge the data into the template.

For example, take the following example with a JavaScript object defined as follows:

var data = {Firstname: 'Fred', Lastname: 'Jones'};

And a template defined as follows:

var template = 'Hello {Firstname} {Lastname}';

When you merge the data into the template, you will get a string that looks like this:

Hello Fred Jones

Of course, in a real application the template will likely also include HTML markup. For example:

var template = 'Hello {Firstname} <span class="class1">{Lastname}</span>';

 Array Data

The data for the template can be an array of objects, in which case the template will be expanded for each object in the array.

For example:

var data = [
    {Firstname: 'Fred',  Lastname: 'Jones'},
    {Firstname: 'John',  Lastname: 'Smith'},
    {Firstname: 'Sally', Lastname: 'Rome'}
];

Using the same template as above, this will produce this output:

Hello Fred Jones
Hello John Smith
Hello Sally Rome

 Object Data

The data object can include sub-objects. For example:

var data = {Firstname: 'Fred', Lastname: 'Jones', Address: { Street: '123 Main St', City: 'Boston', State: 'Ma' }};

The template to consume this data could then be defined as follows:

var template = 'Hello {Firstname} from {Address.Street} in {Address.City}';

Alternatively, the template could be defined as follows:

var template = 'Hello {Firstname} from {Address} {Street} in {City} {/Address}';

Notice that the template includes {Address} and {/Address}. Inside the {Address} block, the placeholders do not need to be fully qualified. You can use {Street}, rather than {Address.Street}

The {Address} placeholder is referred to as a 'scope' placeholder.

Both of these templates will produce the same output:

Hello Fred from 123 Main St in Boston

 Array Data and Object Data Combined

In the Array Data example above, the data was in the form of a JavaScript array. In many cases it will be preferable have the data as an object. So, instead of specifying the data for the template as:

var data = [
    {Firstname: 'Fred',  Lastname: 'Jones'},
    {Firstname: 'John',  Lastname: 'Smith'},
    {Firstname: 'Sally', Lastname: 'Rome'}
]

You could specify it as:

var data = {
    Customers: [
        {Firstname: 'Fred',  Lastname: 'Jones'},
        {Firstname: 'John',  Lastname: 'Smith'},
        {Firstname: 'Sally', Lastname: 'Rome'}
    ]
}

The template to consume this data could then be defined as:

var arr = [];
arr.push('{Customers}');
arr.push('Hello {Firstname} {Lastname}');
arr.push('{/Customers}');
var template = arr.join('\n');
The template is created by pushing individual lines into a JavaScript array and then joining the array. This is simply a convenient technique for creating long strings in JavaScript.

This template will produce this output:

Hello Fred Jones
Hello John Smith
Hello Sally Rome

Notice that since the {Firstname} and {Lastname} placeholders are nested inside the {Customer}...{/Customer} scope placeholders, it is not necessary to use fully qualified placeholders (e.g. {Customer.Firstname}).

The benefit of placing your array as a property of an object, rather than at the top level, is that you can then use the {*header} and {*footer} constructs, and you can compute summary information (such as the number of items in the array, an average value of a field in the array, etc).

 Where can Client-side Templates be used?

Client-side Templates are used in the List Control, ViewBox Control, and FormView Control to define how data is formatted and displayed in these controls.

On the client browser, JavaScript can be used to expand a Client-side Template. The A5.u.template.expand() function merges JSON data into a Client-side Template, returning HTML to display in the browser.

var templateString = 'Hello {fname} {lname}';
var data = {fname: 'David', lname: 'Smith'};
var settings = { 
    template: templateString
}
var html = A5.u.template.expand(data,settings);

If no other settings are defined other than the template, you can pass in the template string instead of the settings object. For example:

var templateString = 'Hello {fname} {lname}';
var data = {fname: 'David', lname: 'Smith'};
var html = A5.u.template.expand(data,templateString);

Client-side Templates can also be used in conjunction with Xbasic to generate HTML to display. The a5_merge_JSON_into_template function can be used to merge JSON data into a template defined using Client-side Template syntax. The template can reference JavaScript functions, partial templates, and locally defined CSS styles.

Client-side Template Syntax

 The Scope Placeholder

If the data passed to the template expand function contains nested objects, or arrays, you can use special {scope} placeholders, where scope is the name of nested object or array.

For example, consider the following simple data object:

{name: 'John Smith', address: {street: '1 Main', city: 'Boston'}}

The template to print this could be:

{name}
{address.street}
{address.city}

or more conveniently, a {scope} variable could be used:

{name}
{address}
    {street}
    {city}
{/address}

The {scope} variable acts like an 'with' statement in Xbasic.

Within the {scope}, it is not necessary to fully qualify the placeholder names.

In the case where the data contains a nested array, the {scope} variable indicates that the template should loop over the rows in the array.

For example, consider this data object:

[
    {name: 'John Smith', children: [{name: 'Griffin'}, {name: 'Callie'}]},
    {name: 'Toby Mohan, children: [{name: 'Kyle'}, {name: 'Katy', name: 'Luke'}]}
]

And the following template:

{name}<br>
<ul>
    {children}
        <li>{name}</li>
    {/children}<br>
</ul>

Which will produce this output:

John Smith
  • Griffin
  • Callie
Toby Mohan
  • Kyle
  • Katy
  • Luke

 The Value Placeholder

The value placeholder, {[value]}, is a special placeholder to use when looping over arrays of values, rather than arrays of objects.

In the previous example, the data was specified as:

[
    {name: 'John Smith', children: [{name: 'Griffin'}, {name: 'Callie'}]},
    {name: 'Toby Mohan, children: [{name: 'Kyle'}, {name: 'Katy', name: 'Luke'}]}
]

The nested array is an array of objects.

However, the data could have been specified as:

[
    {name: 'John Smith', children: ['Griffin', 'Callie']  },
    {name: 'Toby Mohan', children: ['Kyle', 'Katy', 'Luke']}
]

In this case, the nested array is an array of values, not of objects.

To emit the data in the array, the template must use the special {[value]} placeholder. For example:

{name}<br>
<ul>
    {children}
        <li>{[value]}</li>
    {/children}<br>
</ul>
The [value] field can be followed by formatting directives, just like any other field. For example: {[value]:number('$#,###.00')}

 Handling Missing Data

In some cases the data you pass in to the template expander will have missing data. The || directive allows you to specify alternative text in the template when a data value is missing.

For example, consider the following data object:

{
    employees: [
        {firstname: 'Fred', lastname: 'Smith', city: 'Boston'},
        {firstname: 'Laura', lastname: 'Linneker'}
    ]
}

The 'city' property has been specified for the first object in the 'employees' array, but not the second.

{employees}
    Employee name: <b>{firstname} {lastname}</b> City: {city||Not available}<br>
{/employees}

The text to display for a missing value is specified in the placeholder after a || delimiter. In the template shown above, the missing text for the {City} property has been specified as 'Not available'.

The template above gives us the following result for our data object:

Employee name: Fred Smith City: Boston
Employee name: Laura Linneker City: Not available

The missing data directive can be combined with formatting directives. For example: {price:number('#.00')||N/A}

 Headers and Footers

Headers and Footer can be added if the data object you pass into to the template expander contains array data. Using the special {*header}...{/*header} and {*footer}...{/*footer} directives, you can add a header or footer as part of the template.

For example, assume we have the following array of JSON data:

{
    employees: [
        {firstname: 'Fred', lastname: 'Smith'},
        {firstname: 'Laura', lastname: 'Linneker'}
    ]
}

This data is an array of two employees. We would like to add the text "Ths is the header - it prints before the first item in the array" and "This is the footer - it prints after the last item in the array". This can be done using the {*header} and {*footer} directives. The template to add the header and footer looks like this:

{employees}
    {*header}
        This is the header - it prints before the first item in the array<br>
    {/*header}
    Employee name: <b>{firstname} {lastname}</b><br>
    {*footer}
        This is the footer - it prints after the last item in the array
    {/*footer}
{/employees}

When the data is processed through the template, this is the resulting output we will see:

This is the header - it prints before the first item in the array
Employee name: Fred Smith
Employee name: Laura Linneker
This is the footer - it prints after the last item in the array

 Empty Arrays

If an array does not contain any entries you can specify alternative text to display. This is done using the {*empty}...{/*empty} directive. The text inside the {*empty} directives will be displayed in the event that an array contains no data.

For example, consider the following sample data:

{
    employees: [
        {firstname: 'Fred', lastname: 'Smith', skills: [ {name: 'JavaScript'},{name: 'CSS'}]},
        {firstname: 'Laura', lastname: 'Linneker', skills: [{name: 'Xbasic'}]},
        {firstname: 'Junior', lastname: 'Programmer', skills: [] }
    ]
}

Notice that only the last array instances does not have any rows in the skills array. If the skills array is empty, we would like to print "No skills yet". Otherwise, we would like to display a list of skills and the total number of skills an employee has. The template defined below demonstrates how this is done:

{employees}
    Employee name: <b>{firstname} {lastname}</b><br>
    <div style="border:solid 1px green; margin-left:50px;">
        {skills}
            {*empty}
                No skills yet
            {/*empty}
            Skill Name: {name}<br>
            {*footer}
                <i>Count of skills: {@countSkills}</i>
            {/*footer}
        {/skills}
    </div>
{/employees}

Using the {*empty} directive, we are able to define what should be displayed if the employee has no skills.

Additionally, we are able to provide a count of skills using the {*footer} directive and a JavaScript function, countSkills (called using the {@countSkills} directive), to do the calculation. The countSkills implementation is below is:

function countSkills(data,context) {
    return data[context].length;
}

When the employee array is processed by the template, we get the result below:

Employee name: Fred Smith
Skill Name: JavaScript
Skill Name: CSS
Count of skills: 2
Employee name: Laura Linneker
Skill Name: Xbasic
Count of skills: 1
Employee name: Junior Programmer
No skills yet

 Conditional Sections

Templates can include conditional sections. Conditionals allow you to choose what to do when specific values, aka conditions, are met. Conditional sections are defined using the following template commands:

  • {*if logicalExpression}

  • {*elseif logicalExpression}

  • {*else}

  • {*endif}

logicalExpression is any JavaScript expression that evaluates to a true/false value. The logicalExpression can refer to data in the current 'row' of data.

For example, consider the following data:

{
    employees: [
        {firstname: 'Fred', lastname: 'Smith', state: 'MA'},
        {firstname: 'Laura', lastname: 'Linneker', state: 'CA'},
        {firstname: 'Junior', lastname: 'Programmer', state: 'MA'},
        {firstname: 'Bill', lastname: 'Lindsey', state: 'NY'}
    ]
}

When an employee's state is "MA" or "CA", we would like to print the text "Employee is based in MA" or "Employee is based in CA", respectively. If they are not in either state, the text "Employee is not based in MA or CA" should be printed instead.

Using conditional template commands, we can check the value of state to determine which text should be printed, as shown in the tempalte below:

{employees}
    Employee name: {[countOneBased]} <b>{firstname} {lastname}</b><br>
    <div style="border: solid 1px blue; margin-left:20px; margin-bottom: 10px;">
        {*if state=='MA'}
            Employee is based in MA
        {*elseif state=='CA'}
            Employee is based in CA
        {*else}
            Employee is not based in MA or CA
        {*endif}<br>
    </div>
{/employees}

When the employees array data is processed, the template produces the result below:

Employee name: 1 Fred Smith
Employee is based in MA
Employee name: 2 Laura Linneker
Employee is based in CA
Employee name: 3 Junior Programmer
Employee is based in MA
Employee name: 4 Bill Lindsey
Employee is not based in MA or CA

 Formatting Directives

You can include formatting directives in the template placeholder to format numeric values and strings, and to format date values. Formatting directives make it easy to present data in alternative formats without modifying the underlying value. For example, converting a string to lower case or displaying a numeric value as currency.

 Numeric Directives

To format a numeric value, use the :number(formattingDefinition) directive in your placeholder.

For example, assume you have a field called Price, which contains this value: 123456.345.

You might define the template to emit this field as follows:

{Price:number('$#,###.00')}

This will result in the following output:

$123,456.35

You can also use the :number() directive to merge strings into templates. For example,, assume that you have a field called Phone, which contains 6715551234.

The placeholder for this field in your template could be defined as:

{Phone:number('=(###) ###-####')}

This will result in the following output:

(617) 555-1234

 Date Directives

To format a date value, use the :date(formattingDefinition) directive in your placeholder.

You can use the following symbols in the formattingDirective.

  • yy - 2 digit year (e.g. 72)

  • yyyy - 4 digit year (e.g. 1972)

  • dd - day with leading 0 (e.g. 04)

  • d - day (e.g. 4)

  • MM - month with leading 0 (e.g. 03)

  • M - month (e.g. 3)

  • Mon - 3 character month abbreviation with capital first letter (e.g. Dec)

  • mon - 3 character month abbreviation (e.g. dec)

  • Month - month name in full, with capital first letter (e.g. December)

  • month - month name in full (e.g. december)

The missing data directive can be combined with formatting directives. For example: {price:number('#.00')||N/A}

 'Friendly' Date Directives

In addition to formatting dates using a date format string, you can also format dates to display using a "Friendly Date" format. Friendly dates are dates shown in relation to the current time. For example, "In 1 hour" or "tomorrow".

The following directives can be used to format dates or strings using a 'friendly' date format:

The 'Friendly' date directives were added to make it easier to utilize the Date.toCalendar and Date.toRelative JavaScript date formatting functions.

The easiest to add a 'Friendly' date directive to a template is with the Friendly Date Format Builder. The Friendly Date Format Builder will be shown when you select "'Friendly' date format" from the Insert Directive menu in the List, FormView, ViewBox, or ControlBar builders:

The Insert format directive menu in the List Control
The Friendly Date Format Builder

 Text Directives

There are several directives that can be used for string values. These include:

  • :lowercase
  • :uppercase
  • :sentencecase
  • :titlecase

 :lowercase and :uppercase

You can use the :uppercase and :lowercase directives to force string values to upper or lower case.

For example:

{name:uppercase}
{name:lowercase}

 Using :sentencecase and :titlecase

In addition to :uppercase and :lowercase, you can also use the :sentencecase and :titlecase directives to style text. These directives can be used to format strings with upper and lowercase letters.

:sentencecase will capitlize the first letter of the first word in the string. All other characters will be lower case. For example, "John Doe" becomes "John doe" when the field that contains the value (e.g. Fullname) is formatted using :sentencecase:

{fullname:sentencecase}

:titlecase will capitalize the first letter of every word (and lower case all others.) For example, assume a field called "title" contains the value "Getting started with client-side templates". This text can be formatted to read "Getting Started With Client-side Templates" using the :titlecase directve as shown below:

{title:titlecase}

 Expressions

Placeholders in a template can contain arbitrary JavaScript expressions. For example, assume that you have the following data:

{product: 'Book', qty: 4, price: 23}

Suppose you would like to print product in all caps. Since product is a string data type, the .toUpperCase() JavaScript method can be used to convert the whole string to upper case. EG:

{product.toUpperCase()}

Math can also be performed on numeric data. For example, the total price can be calculated and the result formatted to include currency symbols. EG:

{price * total:number('$#,###.00')}

Combining the two examples above, we can create a template that outputs the product name, the price, quantity, and the total amount:

Product: {product.toUpperCase()} - Price: {price}, Quantity: {qty} - Total: {price * total:number('$#,###.00')}

Our template gives us the following:

BOOK - Price 23, Quantity: 4 - Total $92.00

 Functions

Your template can include calls to JavaScript functions that compute values based on data in the current 'row'. To call a function you use the {@JavaScriptFunctionName} placeholder in your template, where JavaScriptFunctionName is the name of the JavaScript function that you want to call.

The value returned by the function is emitted for the placeholder.

The JavaScript function takes a single parameter, data, which allows you to reference data from the current row.

Consider the following simple data object:

{firstname: 'John', lastname: 'Smith'}

And the following template:

Hello {@fullname}

The fullname JavaScript function might be defined as:

function fullname(data) {
    return data.firstname + ' ' + data.lastname.toUpperCase();
}

The template result for the above data, template and JavaScript function will be:

Hello John SMITH

Functions can also be used to compute summary data. For example, assume that the data object you define includes an array of data. You might want to output summary data that includes (say) the count of the number of rows in the array and the total of one of the fields in the array.

Consider the following sample data object:

{
    customer: [
        {name: 'Smith', amountDue: 345.34},
        {name: 'Jones', amountDue: 35.43},
        {name: 'King', amountDue: 45.14}
    ]
}

And the following template:

{customer}
    {name} - {amountDue}<br>
    {*footer}
        Total amount due: {@amountDue} from {@count} customers.
    {/*footer}
{/customer}

And the following definition for the 'amountDue' and 'count' JavaScript function:

function amountDue(data,scope) {
    var arr = data[scope]
    var tot = 0;
    for(var i = 0; i < arr.length; i++) {
        tot = tot + arr[i].amountDue;
    }
    return $u.n.round(tot,2);
}

function count(data,scope) {
    return data[scope].length;
}

The above will produce this output:

Smith - 345.34
Jones - 35.43
King - 45.14
Total amount due: 425.91 from 3 customers.

In the above example, notice that the 'amountDue' and 'count' functions are in the {*footer}..{/*footer} block inside the {customer}..{/customer} scope. When the JavaScript functions are called from inside a {*header} or {*footer} block the scope ('customer') is passed into the function along with the data. ('data' is the first argument, and 'scope' is the second argument).

What's in the 'data' object passed into a JavaScript function depends on where the JavaScript function is called from.

If a JavaScript function is called from within a {*header} or {*footer} placeholder the data in the data object will be the same as if the function had been called from outside the scope. In other words, if the template has {@myfunction} after the closing {/customer} placeholder, the data passed to the 'myfunction' JavaScript function will be the same as the data that would be passed to the function had it been called from inside a {*header} or {*footer} block inside the scope (e.g. {customer}...{/customer}.

On the other had, if the {@myfunction} placeholder is used anywhere within the scope (e.g. inside the {customer} ..{/customer} block), but not inside a {*header} or {*footer} block, the data object only contains data for the current array instance and the 'scope' value is blank.

The following example will help make this clear:

Data:

{
    customer: [
        {name: 'Smith', amountDue: 345.34},
        {name: 'Jones', amountDue: 35.43},
        {name: 'King', amountDue: 45.14}
    ]
}

Template:

{customer}
    {name} - {amountDue} - {@data}<br>
    {*footer}
        In footer:<br>{@data}
    {/*footer}
{/customer}
<br>
Outside the 'Customer' scope: {@data}

JavaScript Function:

function data(data,scope) {
    if(scope == '') scope = 'BLANK';
    var json = JSON.stringify(data,'\t');
    var msg = '<div style="border: solid 1px red;"><b>scope</b>: ' + scope +       

                '<br><b>data</b>: ' + json + '</div>';
    return msg;
}

The above produces this output:

Smith - 345.34 -
scope: BLANK
data: {"name":"Smith","amountDue":345.34}

Jones - 35.43 -
scope: BLANK
data: {"name":"Jones","amountDue":35.43}

King - 45.14 -
scope: BLANK
data: {"name":"King","amountDue":45.14}

In footer:
scope: customer
data: {"customer":[{"name":"Smith","amountDue":345.34},{"name":"Jones","amountDue":35.43},{"name":"King","amountDue":45.14}]}

Outside the 'Customer' scope:
scope: BLANK
data: {"customer":[{"name":"Smith","amountDue":345.34},{"name":"Jones","amountDue":35.43},{"name":"King","amountDue":45.14}]}

Note that the 'data' JavaScript function simply shows the data that is passed into the function (as a JSON string) and also shows the value of the 'scope' argument.

The 'data' function is called in 3 different places:

  • Inside the {customer} scope - (i.e. once for each customer in the array)

  • Inside the {*footer} block (inside the {customer} scope

  • Outside the {customer} scope

As show, inside the {customer} scope, the data passed into the function is just the data for the current array instance and the 'scope' passed into the function is blank.

However, when the JavaScript function is called from inside the {*footer} block, the data and scope passed into the function are the same as if the function had been called from outside the {customer} scope. In this case the data passed into the function includes all of the data in the scope.

With the above understanding of the what's passed into the JavaScript function, let's re-examine the 'amountDue' and 'count' functions from the previous example. Here is the function definition again:

function amountDue(data,scope) {

    var arr = data[scope]
    var tot = 0;
    for(var i = 0; i < arr.length; i++) {
        tot = tot + arr[i].amountDue;
    }
    return $u.n.round(tot,2);
}

The amountDue function has been called from inside a {*footer} construct. Therefore the data passed into the function looks like this (in JSON format)

{"customer":[{"name":"Smith","amountDue":345.34},{"name":"Jones","amountDue":35.43},{"name":"King","amountDue":45.14}]}

Therefore we can get the array of data shown in the {customer} scope by using this JavaScript statement:

data['customer']

However, the 'scope' variable that was also passed into the function contains 'customer', so we can get the array of data as follows:

data[scope]

Once we have the array of data, it is a simple matter of writing a JavaScript loop to sum up the value in the 'amountDue' property for each row in the array.

Note that before we return the number we use the $u.n.round() function from the Alpha Anywhere JavaScript library to round the result to 2 decimal places.

The 'count' function is even simpler. We simply return the length of the array.

function count(data,scope) {
    return data[scope].length;
}

Here is a more complex example that shows an object with two arrays of data - 'charges' and 'payments'. Our template shows the total charges, total payments, and the net amount due (total charges - total payments)

Here is the data:

{
    charges: [
        {name: 'Smith', amount: 345.34},
        {name: 'Jones', amount: 35.43},
        {name: 'King', amount: 45.14}
      ],
    payments: [
        {name: 'Smith', amount: 123.34},
        {name: 'Jones', amount: 45.45}
        ]
}

Here is the template:

<b>Charges</b><br>
{charges}
    {name}<br>
    {*footer}
        Total charges: {@totaldue}
    {/*footer}
{/charges}<br>

<br>
<b>Payments</b><br>
{payments}
    {name}<br>
    {*footer}
        Total payments: {@totaldue}
    {/*footer}
{/payments}<br>
<br>
Net amount due: {@netdue}

Here is the JavaScript:

function totaldue(data,context) {
    var tot = 0;
    var arr = data[context];
    for(var i = 0; i < arr.length; i++) {
        tot = tot + arr[i].amount;
    }
    return tot.toFormat('$#,###.00');
}

function netdue(data,context) {
    var arr = data['charges'];
    var totDue = 0;
    for(var i = 0; i < arr.length; i++) {
        totDue = totDue + arr[i].amount;
    }

    arr = data['payments'];
    var totPay = 0;
    for(var i = 0; i < arr.length; i++) {
        totPay = totPay + arr[i].amount;
    }

    var netDue = totDue - totPay;
    return netDue.toFormat('$#,###.00');

}

And here is output produced by this template:

Charges
Smith
Jones
King
Total charges: $425.91

Payments
Smith
Jones
Total charges: $168.79

Net amount due: $257.12

Notice in the above example that the same 'totalDue' function can be used to return both the total charges and the total payments (because in the first case the 'scope' passed into the function will be 'charges' and in the second case, the 'scope' passed into the function will be 'payments'.

The 'netDue' function that called from outside both the 'charges' and 'payments' scope gets called with the data for both arrays. This function gets the charges array using this syntax:

data['charges']

and then computes the total charges.

Then it gets the payments array, using this syntax:

data['payments']

and then computes the total payments.

Once the total charges and total payments are computed, the net amount due can be computed.

 Emitting Array Instance Data

When you are outside a scope that references array data, you can use a special syntax in the scope placeholder to display values from the scoped array.

For example, consider the following output from a template:

Showing employees from: 'Smith' to 'Programmer'
Employees

Employee name: 1 Fred Smith
skillName: 1 JavaScript
skillName: 2 CSS
Count of skills: 2
Employee name: 2 Laura Linneker
skillName: 1 Xbasic
Count of skills: 1
Employee name: 3 Junior Programmer
No skills yet

Notice that before the Employees are shown, the template shows:

Showing employees from: 'Smith' to 'Programmer'

Where 'Smith' is a value from the first row in the array, and 'Programmer' is a value from the last row in the array.

In the above example, the following template was defined:

Showing employees from: '{employees[0].lastname}' to '{employees[-1].lastname}' <br>
{employees}
    {*header}
        <b>Employees</b><br>
    {/*header}
    Employee name: {[countOneBased]} <b>{firstname} {lastname}</b><br>
    <div style="border:solid 1px green; margin-left:50px;">
        {skills}
            {*empty}
                No skills yet
            {/*empty}
            skillName: {[countOneBased]} {name}<br>
            {*footer}
                <i>Count of skills: {@countSkills}</i>
            {/*footer}
        {/skills}
    </div>
{/employees}

Notice that outside the {employees} scope, the following template directives can be used to emit data from the employees array:

{employees[0].lastname} - 'lastname' property from the 1st array instance.

{employees[-1].lastname} - 'lastname' property from the last array instance.

 Partial Templates

Partial templates are named sub-templates. A template can reference these partial templates using the {*partial partialName} command. This is useful if a template has text that is repeated. For example, consider the following JavaScript code:

//define the data
var _d = {firstname: 'Fred', lastname: 'Smith'}

//define the template
var arr = [];
arr.push('Welcome<br>');
arr.push('Hello {firstname} {lastname}<br>');
arr.push('{*partial partial1}');

var _t = arr.join('\n');

//define the settings object (template and partials)
var settings = {
    template: _t,
    partials: {
        partial1: 'from partial1: {firstname} {lastname}<br>'
    }
}

//merge the data into the template
var html = A5.u.template.expand(_d,settings);

This will produce the following output:

Welcome
Hello Fred Smith
from partial1: Fred Smith

 Root Placeholder for Array Data

Previously if the data you passed into the A5.u.template.expand() function was an array (as opposed to an object with an array as property of the object), you could not use the {*header} and {*footer} directives in your template to generate a header or footer for the array data.

Now, you don't have to artificially structure your data as a top level object with an array as a property of the object. The {*root} tag is now supported when the data passed in is an array.

Consider the following sample data:

var data = [
    {firstname: 'Fred', lastname: 'Smith'},
    {firstname: 'John', lastname: 'Jones'}
]

and the following template:

var template = [
                            '{*root}',
                            '{*header}There are {root.length} people<br />{/*header}',
                            '{firstname} {lastname}<br />',
                            '{*footer}Count: {root.length} {/*footer}',
                            '{/*root}'
                        ].join('');

This data and template will product this output:

There are 2 people
Fred Smith
John Jones
Count: 2

Because the template uses the {*root} tag, the data that is passed in is implicitly restructured as:

var data = {
    root: [
            {firstname: 'Fred', lastname: 'Smith'},
            {firstname: 'John', lastname: 'Jones'}
           ]
    }

 Escaping the { and } Characters in a Template

The { and } characters in a template indicate placeholders for merged data. If you want to explicitly output either of these characters you can escape them using a leading backslash.

Assume that the data you pass to the template expander is:

{firstname: 'Fred'}

Assume that you want the output from the template to be

{Fred}

You would define your template as:

\{{firstname}\}

 Pre-processing Array Data

You can now pre-process arrays before they are expanded.

For example, assume you have the following data:

var data = {
    company: [
        {firstname: 'Fred', lastname: 'Smith'},
        {firstname: 'John', lastname: 'Jones'},
        {firstname: 'Andrea', lastname: 'Jones'},
        {firstname: 'Margo', lastname: 'Jones'}
    ]
}

And the following template:

var template = [
    '{company}',
    '{firstname} {lastname}<br>',
    '{/company}'
].join('');

This template and data combination will result in this output:

Fred Smith
John Jones
Andrea Jones
Margo Jones

But, say you only wanted to output the top two customers. To do this you would need to pre-process the company array before it was expanded. You can specify the name of a function to call before an array is expanded using the @functionName directive in the placeholder for the array.

For example, notice in the template below, the placeholder for the company array specifies that the processCompany function should be called before the data are expanded:

var template = [
    '{company@processCompany}',
    '{firstname} {lastname}<br>',
    '{/company}'
].join('');

The array pre-processor function gets called with three arguments

  • data - the array that is being expanded

  • temp - a global storage object that will exist for the entire length of time that the template is being expanded. You can use this object to capture information to be output later in the template.

  • root - the root data object (i.e. all of the data)

Your array pre-processor function must return the array that you want to render.

Here is how the processCompany array pre-processor function could be defined:

function processCompany(data,temp,root) {
    //limit to first two items in the array
    data = data.slice(0,2);
    return data;
}

The resulting output is now:

Fred Smith
John Jones

Now assume you want to first sort the data by the 'firstname' property, then return the top two entries. In this case, the array pre-processor function would be defined as:

function processCompany(data,temp,root) {
    //sort on the 'firstname' property
    data.sort( function(a,b) {
        if (a.firstname < b.firstname) return -1;
        if (a.firstname > b.firstname) return 1;
        return 0;
        }
    )

    //limit to first two items in the array
    data = data.slice(0,2);
    return data;
}

The resulting output is now:

Andrea Jones
Fred Smith

An important use for the array pre-processor function is to compute summary data for the array. See Using the Special "root" and "temp" Variables in Templates below. You can compute all of the summary values your template needs in a single function, rather than defining separate functions for each summary value your template outputs.

 Using the temp and root Template Variables

You can use the special "root" and "temp" variables in a template by enclosing them in [ and ].

The example below demonstrates the use of both [root] and [temp] in the template.

TIP: To watch a video of how the template used in this example was developed, watch this video:

For example, consider the following data set:

var data = [
    {
        orderId: 1,
        date: '1/1/2014',
        orderItems: [
                {itemId: 1, qty: 3, price: 23.4},
                {itemId: 23, qty: 2, price: 3.3},
                {itemId: 7, qty: 5, price: 5.3}
            ]
    },
    {
        orderId: 2,
        date: '1/2/2014',
        orderItems: [
            {itemId: 31, qty: 7, price: 3.8},
            {itemId: 17, qty: 4, price: 9.2}
        ]
    },
    {
        orderId: 3,
        date: '1/5/2014',
        orderItems: [
            {itemId: 11, qty: 9, price: 13.3},
            {itemId: 27, qty: 2, price: 19.2},
            {itemId: 6, qty: 19, price: 3.6},
            {itemId: 7, qty: 22, price: 9.1}
        ]
    }
]

The data are an array of order objects. Each order has a nested array called 'orderItems' that have the order items for that order.

Assume that you would like the template to render as shown below:

Order Id: 1 (Order number 1 of 3 orders - for orders placed between 1/1/2014 and 1/5/2014)
Order Date: 1/1/2014
<td></td>
# Item Id Quantity Price Extended Total
1 1 3 $23.40 $70.20
2 23 2 $3.30 $6.60
3 7 5 $5.30 $26.50
Count: 3Avg Quantity: 3.33 Avg Price $10.67 Total: $103.30

Order Id: 2 (Order number 2 of 3 orders - for orders placed between 1/1/2014 and 1/5/2014)
Order Date: 1/2/2014
<td></td>
# Item Id Quantity Price Extended Total
1 31 7 $3.80 $26.60
2 17 4 $9.20 $36.80
Count: 2Avg Quantity: 5.50 Avg Price $6.50 Total: $63.40

Order Id: 3 (Order number 3 of 3 orders - for orders placed between 1/1/2014 and 1/5/2014)
Order Date: 1/5/2014
<td></td>
# Item Id Quantity Price Extended Total
1 11 9 $13.30 $119.70
2 27 2 $19.20 $38.40
3 6 19 $3.60 $68.40
4 7 22 $9.10 $200.20
Count: 4Avg Quantity: 13.00 Avg Price $11.30 Total: $426.70

Grand total for all 3 orders is: $593.40

There is a lot going on in this example. The template for this example is shown below.

Items of note in this example include:

  •  Extended Total for All Items

    While the data for each orderItem does not contain an extendedPrice property, the extended price is shown for each item and it is formatted with a $ and two decimal places.

    If you look at the template below you will notice that the template for the 'Extended Total' column is defined as:

    {qty*price:number(\'$#,###.00\')}
  • The template defines the expression qty * price and then uses the :number() directive to format is with a $ and two decimal places (and a comma for the thousands separator - not shown in the output because none of the extended totals are above $999.99).

  •  Order Items in Tables

  •  Summary Data in Table Footer

    The footer row for the Order Items table displays the count of the number of items, the average quantity, the average price and the total for the order. All of these summary values are computed in the array pre-processor function (orderItemsPreprocessor). Notice that the tag for the orderItems array {orderItems@orderItemsPreProcessor} specifies the name of the pre-processor function.

    The array pre-processor function puts these summary values in the special 'temp' variable. The function adds the following properties to the 'temp' variable:

    • temp.grandTotal - the grand total for all of the orders

    • temp.averagePrice - the average price for each order

    • temp.averageQuantity - the average quantity for each order

    • temp.totalExtendedPrice - the order total for each order

    • temp.itemCount - the number of items in each order

  • In order to output these computed values in the template, the special [temp] variable is used. For example, to show the average price (formatted with $ and two decimal places), the template includes:

    {[temp].averagePrice:number(\'$#,###.00\')}
  •  Numbered Rows

    The rows in the Order Items table are numbered. The special {[countOneBased]} tag is used to number the rows.

  •  Order Number and Total Orders

    Each order specifies the order number and the number of orders (e.g. Order number 2 of 3 orders). The template looks like this:

    (Order number {[countOneBased]} of {[root].length} orders
  • In order to output the order number (i.e. 1 for the first order, 2 for the 2nd order, etc.), the special {[countOneBased]} tag is used.

    In order to output the total number of orders, the length property of the special [root] variable is used. Because the data passed into the template expand function is an array, the 'root' object is implicitly created (see section above 'The {*root} Tag ')

  •  Order Date

    Each order also indicates the date for the first order and last order displayed. For example:

    (Order number 2 of 3 orders - for orders placed between 1/1/2014 and 1/5/2014)

    The template the date range is:

    - for orders placed between <b>{[root][0].date}</b> and <b>{[root][-1].date}</b>
  • To get the date of the first order the special [root] variable is used with an index of 0. So [root][0] has the data for the first order and [root][0].date is the date for the first order.

    To get the date for the last order, we again use the special [root] variable, but this time with an index of -1. So [root][-1].date is the date for the last order.

  •  Grand Total Template

    To print the grand total of all of the orders, the template is:

    Grand total for all {[root].length} orders is: {[temp].grandTotal:number(\'$#,###.00\')}
  • The count of the number of orders can be obtained using the length property of the special [root] variable. The grand total of all orders is output using the grandTotal property that was added to the 'temp' variable by the 'orderItems' array pre-processor function. To output a value in the special 'temp' variable, we use [temp].

    The 'temp' variable is not scoped to each invocation of the array pre-processor function. It it therefore ideal for accumulating aggregate values for all of the data. You will notice that in the pre-processor function, when the first order is processed, the temp.grandTotal variable does not yet exist. It is therefore initialized to 0. Then, the order total for the current order being processed is added into the temp.grandTotal variable.

  • Here is the template for this example:

    var template = [
        '{*root}',
            'Order Id: {orderId} (Order number {[countOneBased]} of {[root].length} orders - for orders placed between <b>{[root][0].date}</b> and <b>{[root][-1].date}</b>) <br>',
            'Order Date: {date}<br>',
            '{orderItems@orderItemsPreProcessor}',
                '{*header}',
                    '<table border="1" cellspacing="1" cellpadding="3" style="border-collapse: collapse;" >',
                    '<tr>',
                        '<th>#</th>',
                        '<th>Item Id</th>',
                        '<th>Quantity</th>',
                        '<th>Price</th>',
                        '<th>Extended Total</th>',
                    '</tr>',
                '{/*header}',
                '<tr>',
                    '<td>{[countOneBased]}</td>',
                    '<td>{itemId}</td>',
    
                    '<td style="text-align:right;">{qty}</td>',
                    '<td style="text-align:right;">{price:number(\'$#,###.00\')}</td>',
                    '<td style="text-align:right;">{qty*price:number(\'$#,###.00\')}</td>',
                '</tr>',
                '{*footer}',
                    '<tr>',
                        '<td>Count: {[temp].itemCount}</td>',
                        '&lt;td&gt;&lt;/td&gt;',
                        '<td>Avg Quantity: {[temp].averageQuantity:number(\'#.00\')}</td>',
                        '<td>Avg Price {[temp].averagePrice:number(\'$#,###.00\')}</td>',
                        '<td style="text-align:right;">Total: {[temp].totalExtendedPrice:number(\'$#,###.00\')} </td>',
                    '</tr>',
                    '</table><br>',
                '{/*footer}',
            '{/orderItems}',
            '{*footer}',
                'Grand total for all {[root].length} orders is: {[temp].grandTotal:number(\'$#,###.00\')}<br>',
            '{/*footer}',
        '{/*root}'
    ].join('');

    The array pre-processor function for the 'orderItems' array is defined as follows:

    function orderItemsPreProcessor(data,temp,root) {
        var totalQuantity = 0;
        var totalPrice = 0;
        var totalExtendedPrice = 0;
        var extendedPrice = 0;
        var _d = '';
        for(var i = 0; i < data.length; i++) {
            _d = data[i];
            extendedPrice = _d.qty * _d.price;
            totalExtendedPrice = totalExtendedPrice + extendedPrice;
            totalPrice = totalPrice + _d.price;
            totalQuantity = totalQuantity + _d.qty;
        }
    
        //the first time this function is called temp.grandTotal will be undefined
    
        //so we initialize it to 0
        if(typeof temp.grandTotal == 'undefined') temp.grandTotal = 0;
    
     
    
        //we can then accumulate the grand total for all orders
        temp.grandTotal = temp.grandTotal + totalExtendedPrice;
     
    
        temp.averagePrice = (totalPrice / data.length);
        temp.averageQuantity = (totalQuantity / data.length);
        temp.totalExtendedPrice = totalExtendedPrice;
        temp.itemCount = data.length;
        //it is CRITICAL that the pre-processor function return an array to be expanded
        //even though this function has not modified the data in the 'data' array,
    
        //it must still return the 'data' array
        return data;
    }

     Templates Can Reference Functions in Any Namespace and Can Take Arguments

    Previously, when a template referenced a function, the function needed to be defined as a global function. Now, templates can reference functions an arbitrary namespaces.

    Consider the following simple example:

    Data:

    var data = [
        {firstname: 'Fred', lastname: 'Smith'},
        {firstname: 'John', lastname: 'Jones'}
    ]

    JavaScript function definition (Notice that the function takes two arguments and is in the 'obj' namespace.):

    var obj = {
        fullname: function(fn,ln) { return 'Hello <b>' + fn + ' ' + ln + '</b>'}
    }

    Template (Notice that the template calls the function in the 'obj' namespace and passes in argument values from the current row.):

    var template = '{firstname} {lastname} Function call is output here: {@obj.fullname(firstname,lastname)}<br>'

    The output HTML produced is:

    Fred Smith Function call is output here: Hello Fred Smith
    John Jones Function call is output here: Hello John Jones

     Defining Custom Formatters

    Templates can format data before it is output using the built-in format directives. For example, you can format a number so that it has $ and two decimal places by using the :number() format directive. For example:

    {price:number(\'$#,###.00\')}

    However, you can also define your own custom format directives. This is done by adding functions to the A5.u.template.formats object.

    Consider the following example:

    Data:

    var data = {
        company: [
            {firstname: 'Fred', lastname: 'Smith', phone: 7815551234},
            {firstname: 'John', lastname: 'Jones', phone: 2125551234},
            {firstname: 'Jane', lastname: 'Jones', phone: 4155551234},
            {firstname: 'Margo', lastname: 'Jones', phone: 4325551234}
        ]
    }

    Template:

    var template = [
        '{company}',
        '{firstname:formatName(\'l\')} {lastname:formatName(\'u\')} {phone:phoneNumber}<br>',
        '{/company}'
    
    ].join('');

    Notice the template uses custom formatters for the each of the fields. Both 'firstname' and 'lastname' use a custom formatter called 'formatName' and in one case an argument value of 'l' is passed in (to format the data as lower case) and in the second case, an argument value of 'u' is passed in (to format the data as upper case).

    The 'phone' field uses a custom formatter called 'phoneNumber'. This formatter does not take any arguments.

    To define these two custom formatters, the following JavaScript is used to add the format functions to the A5.u.template.formats object:

    A5.u.template.formats.formatName = function(value,flag) {
        if(flag == 'u') return value.toUpperCase();
        else if(flag == 'l') return value.toLowerCase();
        else return value;
    }
    
    A5.u.template.formats.phoneNumber = function(value) {
        return value.toFormat('=(###)-###-#####');
    }

    Notice that the custom format functions all get passed 'value' - the value to be formatted.

     Arguments Passed to Template Functions

    If a template function does not explicitly specify arguments, the implicit arguments passed to the function now include the special 'temp' and 'root' variables.

    For example, consider the following template snippet:

    {@myCustomFunction}

    This template function does not specify any arguments. Therefore the 'implicit' arguments for the function called are:

    • data

    • context

    • temp

    • root

    Previously, only 'data' and 'context' were passed to the function.

    Where context is a string with the name of the variable that the function was being called in the context of. For example, in a {*header} or {*footer} directive for an array called 'company' the 'context' would be 'company'. However in an individual item in the company array, the context would be blank (as the context is implicit).

     Comments

    Comments can be added to Client-side Templates using the {/* ... */} directive.

    {/* this is a comment */}
    Comments must have white space after the first "*" and before the last "*".

    When the template is expanded, any space taken by comments is ignored. Comments are useful for the obvious use case of adding explanations to your templates.

    But another less obvious use case for comments is to make templates more readable. For example, in certain cases your templates should not include any line breaks because the space taken by the line break will affect how the expanded template appears. However, this makes the template difficult to read. In the example below, an empty comment that spans two lines is used to insert a line break into the template for readability. This line break is ignored, however, when the template is expanded.

    {/* this is the firstname field */}
    <div class="item">{/*
    */}<div class="label">Firstname</div>{/*
    */}<div class="field">{firstname}</div>{/*
    */}</div>

     Arbitrary iterator

    The arbitrary iterator allows you to iterate over dynamically created data.

    {*@functionName}
    ...
    {/*}

    The iterator is useful for inserting external data into the template when it is expanded.

    {orders}
    {id} {total} {date}<br />
    {*if !delivered}
    {*@tracking}
    {location} at {date}
    {/*}
    {*endif}
    {/orders}

    Where tracking is a JavaScript function that might be defined as follows:

    function tracking(data,temp,root,parent,path){
        /*
        data is the current parent data context - in this case the current order
        assume code here that goes to a separately stored tracking array and
        returns an array of locations and dates
        */
    
        return [
            {location: 'Boston MA', date: '9/4/2016'},
            {location: 'Albany NY', date: '9/5/2016'}
        ];
    }

     Escape

    You can escape long strings of template characters using the escape directive:

    <escape<{some template code'}>>

    In some cases you need to include special functions in a template (such as A5.map.staticMap() - a function that generates a static HTML map). The function call may include characters such as a colon, which must be escaped because they have special meaning in a template.

    {A5.map.staticMap(<escape<{
    width: 400,
    height: 300,
    center: 'Boston MA'
    }>>)}

    Before the escape directive was available, this above template would have had to be written as:

    {A5.map.staticMap(\{width\: 400,height\: 300,center\: 'Boston MA'\})}

     Ensure

    The ensure directive allows you to ensure certain properties exist in the Data.

    {*ensure a,b,c}

    Where a, b and c are data variables that you want to define so that if they are referenced in an {*if} block, the {*if} block will evaluate correctly.

    For example, assume the data passed to a template is:

    {bar: 'world'}

    And the template is defined as:

    {*if foo == 'hello'}
    ...
    {*elseif bar == 'world'}
    ...
    {*else}
    ...
    {*endif}

    Without {*ensure foo}, the {*if} block will not execute correctly, as foo doesn't exist.

    Here is how the template could be rewritten to ensure that it works as expected:

    {*ensure foo}
    {*if foo == 'hello'}
    ...
    {*elseif bar == 'world'}
    ...
    {*else}
    ...
    {*endif}

     Defined/Undefined

    The defined and undefined directives provide a shorthand syntax for checking if a list of variables are or are not defined.

    It is common in the logical expressions in a {*if} block to check that certain variables are defined or undefined. The code to do this can be cumbersome and the <defined<variableList>> and <undefined<variableList>> directives make your template more readable.

    For example, imagine you had the following template:

    {*if typeof variableName1 == 'undefined' || typeof variableName2 == 'undefined'}
    ...
    {*endif}

    The syntax in the template is cumbersome and can be simplified as:

    {*if <undefined<variableName1,variableName1>>}
    ...
    {*endif}

    Similarly, an expression that tests if variables are all defined might previously have been written as:

    {*if typeof variableName1 != 'undefined' && typeof variableName2 != 'undefined'}
    ...
    {*endif}

    can now be written as

    {*if <defined<variableName1,variableName2>>}
    ...
    {*endif}

     Expression Escapement

    The ":" and "|" characters have special meaning in a template. If they are used in an expression, they must be escaped.

    If a logical expression in an immediate if expression is enclosed in ( ... ), the special : and | characters do not need to be escaped.

    The : character in a template is how a formatter is defined and the || character sequence is used to define default text for null values.

    For example consider the following template code:

    {a ? b \: c:uppercase}

    If a is true, then the expression will return b. However, if a is false, the expression will return c. The result of the expression a ? b \: c will then be converted to uppercase (expressed as :uppercase.)

    The above template can now be rewritten as:

    {(a ? b : c):uppercase}

    Expression escapement is not limited to logical expressions. For example, the following template expression concatenates strings, one of which is a string containing a : character.

    {(a + ':' + b):uppercase}

    Examples

     <ul> Example

    This example demonstrates using HTML unordered lists with Client-side Templates.

    The <ul></ul> tags define an unordered list. <li></li> tags placed inside the list tags define an entry in the list

    <ul>
           <li>Entry 1</li>
           <li>Entry 2</li>
           <li>Entry 3</li>
       </ul>

    Result:

     Combining a List with JSON Data

    You can open Alpha Anywhere's 'Template Tester' from the 'Tools' dropdown in the Web Projects Control Panel. Say you had the following data object in the template tester's 'JSON Data' pane.

    [
        {name: 'John Smith', children: [{name: 'Griffin'}, {name: 'Callie'}]},
        {name: 'Toby Mohan', children: [{name: 'Kyle'},{name: 'Katy'},{name: 'Luke'}]}
    ]

    On the 'Template' pane you can reference the name field by using {name}. Because the 'children' field is a JSON array it can be referenced on the 'Template' pane using {children}{/children}. Inside the children array there is also a 'name' property for of the child in the children array. These can be accessed by placing {name} inside the {children}{/children} tags.

    {name}
        {children}
            {name}
        {/children}

    This will display all of the JSON data in a unformatted structure. You can add the html <ul></ul> and <li></li> tags to structure the data into a list.

    {name}<br>
    <ul>
        {children}
            <li>{name}</li>
        {/children}<br>
    </ul>

    Result

     <table> Example

    This example demonstrates using an HTML table in a Client-side Template.

    The <table></table> tags define a table in html. Without defining any cells or styling the table will not be visible. The <tr></tr> tags add a new row to a table when they are placed inside the table. Use <th></th> to add a header cell to a table row.

    <table>
        <tr>
            <th>Table Header</th>
        </tr>
    </table>

    Result:

    Use <td></td> tags to add a regular cell to a table row.

    Result:

    <table>
        <tr>
            <td>Table Cell</td>
        </tr>
    </table>

    You can add borders to a table and stylize them inside the <table> tag.

    <table border="1" cellspacing="1" cellpadding="10" style="border-collapse: collapse;">
        <tr>
            <th>Header</th>
        </tr>
        <tr>
            <td>Cell 1</td>
        </tr>
        <tr>
            <td>Cell 2</td>
        </tr>
    </table>

    Result:

    Additional attributes like 'bordercolor' can be added to the <table> tag as well.

    <table border="1" cellspacing="1" cellpadding="10" style="border-collapse: collapse" bordercolor="blue">
        <tr>
            <th>Header</th>
        </tr>
        <tr>
            <td>Cell 1</td>
        </tr>
        <tr>
            <td>Cell 2</td>
        </tr>
    </table>

    Result:

     Combining a Table with JSON Data

    You can open Alpha Anywhere's 'Template Tester' from the 'Tools' dropdown in the Web Projects Control Panel. Say you have the following 'employees' object defined in the JSON data pane of the Template Tester.

    {
        employees: [
            {firstname: 'Fred', lastname: 'Smith', state: 'MA'},
            {firstname: 'Laura', lastname: 'Linneker', state: 'CA'},
            {firstname: 'Junior', lastname: 'Programmer', state: 'MA'},
            {firstname: 'Bill', lastname: 'Lindsey', state: 'NY'}
        ]
    }

    On the the 'Template' pane of the template tester you can access this data by placing the name of the JSON object inside an opening {} and closing {/}.

    {employees}
    {/employees}

    You can reference a field inside the JSON data by placing the name of the field in {} brackets inside the {employees}{/employees} tags .

    {employees}
                {firstname}
    {/employees}

    This will give you the raw unformatted data. You can use an HTML table inside of the {employees}{/employees} tags to structure the data field, {firstname}, that was added:

    {employees}
    <table border="1" cellspacing="1" cellpadding="10" style="border-collapse: collapse" bordercolor="blue">
        <tr>
            <th>{firstname}<br/></th>
        </tr>
        <tr>
            <td width="50">Cell 1</td>
        </tr>
    </table>
    {/employees}

    Result:

     <div> Example

    This example demonstrates using <div> tags in a template.

    You can stylize <div></div> tags by adding a 'style' definition directly in the opening <div>

    <div style="border:solid 1px green; height:50px; width:200px;"></div>

    Result:

     Using <div></div> Tags with JSON Data

    You can open Alpha Anywhere's 'Template Tester' from the 'Tools' dropdown in the Web Projects Control Panel. Say you have the following JSON data on the template tester's 'JSON Data' pane. Here the JSON data is an object named {employees}

    {
        employees: [
            {firstname: 'Fred', lastname: 'Smith', skills: [ {name: 'Javascript'},{name: 'CSS'}]},
            {firstname: 'Laura', lastname: 'Linneker', skills: [{name: 'Xbasic'}]},
            {firstname: 'Junior', lastname: 'Programmer', skills: [{name: 'Fortran'}] }
        ]
    }

    On the 'Template' pane of the template tester you can access this JSON Data by using the name of the object enclosed in {} brackets.

    {employees}
    {/employees}

    Properties inside the object can be referenced by entering the name of the property in {} brackets inside the {employees}{/employees} tags. Because the 'skills' property is itself an array object, it too will also need a closing tag, like this {skills}{/skills}. The 'name' property inside the skills array can be accessed by placing a {name} placholder inside the {skills}{/skills} tags.

    {employees}
    {firstname} {lastname}
            {skills}
               {name}
            {/skills}
    {/employees}

    This will display some unstructured JSON Data. <div></div> tags can be used to format this data into something easier to read.

    {employees}
        {firstname} {lastname}<br><br>
        <div style="border:solid 1px green; padding:10; margin-left:10px; width:150;">
            {skills}
          {name}<br/>
            {/skills}
        </div><br>
    {/employees}

    Result:

    See Also

    Alpha Software Corporation
    70 Blanchard Road, Burlington, MA 01803-5100, 781.229.4500
    © Copyright 2000-2018 Alpha Software Corporation. All Rights Reserved