The ECMAScript for XML standard a.k.a “E4X” is a cool addition to JavaScript that provides for native support of XML. E4X adds nifty features such as simplified XML traversal and XML literals to the language. E4X is supported in Mozilla and Adobe JavaScript implementations. The lack of support in IE hinders the widespread adoption of E4X. However, E4x is supported on Aptana Jaxer.
I’ve been using Jaxer for web development lately. Jaxer is a web application server which uses JavaScript on the server side. The server-side JavaScript implementation is based on Mozilla, and E4X is supported. So, if you’re using Jaxer, there is nothing holding you back from using E4X on the server side.
With this in mind, I’ve cooked up an example intended to whet your appetite for E4X. The source code is shown below. The example executes a query against a database and renders each returned row as a HTML table. I’m using E4X for two tasks: storing configuration data and filling in a HTML template.
Note how various scripts or functions are tagged with runat='server'. This is how Jaxer knows which scripts to process on the server and which not to. All of the scripts in this example run on the server. The execution of the scripts is started by the onserverload attribute of the body tag. This is the server-side equivalent to the onload attribute in the browser context.
For the configuration example, I stored SQL statements in an XML document. I needed a select query to execute against the database, and then of course I realized I needed some DDL to create the table and some more SQL to insert records. I decided to store all of my queries as E4X XML literals in JavaScript embedded in my HTML page. The embedded XML is seen only on the Jaxer Server, and not in the client browser. On lines 12-37 I used an XML literal to declare an XML document containing 3 SQL statements.
A couple of points to note. First I embedded this configuration into a HTML page. With the Jaxer server-side facilities for accessing the file system, one could store the XML in a file and share the configuration across HTML pages. Second, with a CDATA section (lines 27-29), I don’t have to worry about escaping my SQL to make valid XML.
IMHO, the coolest E4X feature is simplified DOM access. The code to pull out a particular SQL statement by id is:
var sqlToRun = sqlStatements.sql.(@id == 'select-persons-no-heavier-than');
After executing the select query, I render each row as a little HTML table. On line 50 I access each record via the Jaxer.DB.ResultSet API. I create a table for each record with an XML literal on lines 52-69. One nice feature of E4X is the ability to execute arbitrary JavaScript code within the XML literal. For example, line 67:
<td>{(dbRecord.weight_kg / 0.454).toFixed(2)}</td>
reads a JavaScript object property, divides by .45, renders the number with 2 decimal places, and then places that value in the td tag. Finally on line 74, I insert the table into the page’s DOM tree. The insert is done with Prototype’s Element.insert method which takes a string or DOM Node as an argument and inserts the content into the page’s DOM tree.
Don’t forget, that all of the scripts tags (for prototype.js as well) have a runat=”server” attribute, so all of this action happens on the Jaxer server before the page is render as text and sent to the client.
One interesting point is that I used Prototype on the server side. My usage was pretty modest here; I take it as a good sign that the server side JavaScript environment closely matches the JavaScript environment in a browser.
I’m pretty happy with this example code. I think the templating of HTML turned out to be very transparent and easy to understand code. Several factors contribute to clean code: XML literals avoid cluttering the code with quoted string and also allow value interpolation, and insertion of the XML strings into the DOM tree with Prototype. This is a nice illustration of the handy features that are available in the Jaxer server side programming environment.
<html>
<head>
<title>Jaxer E4X Example</title>
/*
* All of the JavaScript is running on the server due to the
* runat="server" attribute of the script tags
*/
<script runat="server" src="lib/prototype/prototype.js"></script>
<script runat="server">
// An XML literal holding template SQL
var sqlStatements =
<sql-statements>
<sql id='create-table'>
create table if not exists person (
id integer,
first_name varchar(20),
last_name varchar(20),
birth_date datetime,
weight_kg decimal(5,2),
height_m decimal(5,2),
constraint person_pk primary key(id)
);
</sql>
<sql id='insert-person'>
insert into person
(id, first_name, last_name, birth_date, weight_kg, height_m)
values
(?, ?, ?, ?, ?, ?)
</sql>
<sql id='select-persons-no-heavier-than'>
<![CDATA[
select * from person where weight_kg <= ?
]]>
</sql>
</sql-statements>;
// Called on server side page load.
// Triggered by the onserverload attribute of the body tag
function showPersonDetails() {
// Find a SQL statement by name
var sqlToRun = sqlStatements.sql.(@id == 'select-persons-no-heavier-than');
// Execute the SQL with the Jaxer.DB API
var resultSet = Jaxer.DB.execute(sqlToRun, 200);
// For each record returned from the DB...
resultSet.rows.forEach(function(dbRecord, index) {
// Create some markup using a XML Literal and value interpolation
var tbl =
<table border='1'>
<tr>
<td colspan="2">Person Detail</td>
</tr>
<tr>
<td>First name:</td>
<td>{dbRecord.first_name}</td>
</tr>
<tr>
<td>First name:</td>
<td>{dbRecord.last_name}</td>
</tr>
<tr>
<td>Weight in pounds:</td>
<td>{(dbRecord.weight_kg / 0.454).toFixed(2)}</td>
</tr>
</table>;
tbl += <br/>;
// add the new markup to the page DOM
$('person-details').insert(tbl.toXMLString());
});
}
</script>
</head>
<body onserverload="showPersonDetails();">
<div id='person-details'>
</div>
</body>
</html>
