Taconite for AJAX integration

Web:Extend includes a Taconite component that allows you to perform simple DOM updates against an XML document either server or client-side.

Taconite is a method to perform multiple DOM updates with an XML document. This document contains instructions which describe the DOM updates.

Here is an example of a Taconite document:

<?xml version="1.0" encoding="utf-8"?>

<taconite>
	<prepend select="#log">
		<li>nox sent you a message.</li>
	</prepend>
</taconite>

This neat document may be used to implement a pseudo real-time activity logger in a social network application. The user agent will then periodically checks the server for new activity and the user will see new items at the beginning of the list #log on the page.

Web:Extend includes mechanisms to generate Taconite documents. It will send a Taconite document only if the context is xmlhttprequest and if you used Taconite, otherwise it sends the template file. When the context is http and you use Taconite anyway, the template will first be rendered as usual, and then the Taconite you sent will be applied to it server-side. This allows you to easily make an event compatible with both contexts without any need for duplicate code.

We recommend using the Taconite mechanisms included in the framework with the jQuery library for client-side processing, because it includes a form plugin and a taconite plugin, that will make using Taconite a real bliss. The jQuery taconite plugin will detect Taconite AJAX responses and act accordingly without you needing to do anything other than initiate AJAX calls.

As the most common updates can be performed both server and client-side, this tool greatly reduces the need of specific and duplicate code for AJAX requests. If the user does not have javascript available, weeTaconite will gracefully handle the request by regenerating the page and applying any DOM update on the server-side; otherwise only the XML Taconite document is sent to the user which is then processed locally. This behaviour lighten the server charge in both processor time and bandwidth volume, as only the XML Taconite is returned.

Usage

We're going to demonstrate Taconite first by writing an update link that will refresh a part of the page using AJAX. The event used to display the page and to update it will be the same.

The following event will do the job for you. For this example we will only display and update the current date and time but know that you can update more than one thing at once.

<?php

class example extends weeFrame
{
	protected function defaultEvent($aEvent)
	{
		if ($aEvent['context'] == 'xmlhttprequest')
			$this->update('replaceContent', '#date', date());
		else
			$this->set('date', date());
	}
}

The template example.tpl can look like this:

<html>
<head>
	<title>Example</title>
	<script type="text/javascript" src="<?php echo APP_PATH?>res/jquery/jquery.js"></script>
	<script type="text/javascript" src="<?php echo APP_PATH?>res/jquery/jquery.taconite.js"></script>
	<script type="text/javascript">
// The following function will be called when the document is loaded
$(function(){
	$('#update').click(function(){
		$.get($(this).attr('href'));
		return false;
	});
});
	</script>
</head>

<body>
	<div id="#date"><?php echo $date?></div>
	<a href="example" id="#update">Update</a>
</body>
</html>

The template displays the date sent by the frame, and define an AJAX action whenever the update link is clicked. There's nothing else to do, Taconite will handle it automatically. Whenever an AJAX request will be sent to the example frame, a Taconite response will replace the contents of the #date div with the current date.

Alternatively, you can also give a template to Taconite. This allows you to easily separate concerns and render the same template for both normal and Taconite response.

Instead of simply giving the date as in the example above, we could give the contents of a template. We'll transform our example above to make it send the date through a template. I know, making a template only to display a date isn't that useful but it should showcase how it works without complicating the example.

The main difference lies in the frame. Instead of giving the date we need to create a template and send its output:

<?php

class example2 extends weeFrame
{
	protected function defaultEvent($aEvent)
	{
		if ($aEvent['context'] == 'xmlhttprequest') {
			$oTpl = new weeTemplate('the_date', array('date' => date()));
			$this->update('replaceContent', '#date', $oTpl->toString());
		} else {
			$this->set('date', date());
		}
	}
}

The template only has the date line to be changed. We just need to render the template instead of outputting directly, like you would do normally when separating concerns into different templates.

<html>
<!-- no change for <head>, skipping it -->
<body>
	<div id="#date"><?php $this->template('the_date')?></div>
	<a href="example" id="#update">Update</a>
</body>
</html>

The template the_date.tpl can then simply output the date.

<?php echo $date?> 

You can also put the #date div inside the new template and use the replace transformation command instead of replaceContent in the frame.

Alongside forms

You can also use Taconite to provide immediate feedback to users submitting form data. Let's take a look at our edit event, responsible for editing a specific resource of our application. The event is written pretty much like a standard form handling but we've added 3 lines for the AJAX specific code. The workflow is exactly the same however, as you can see.

<?php

class example3 extends weeFrame
{
	protected function eventEdit($aEvent)
	{
		$oResource = exResourceSet::instance()->fetch($aEvent['get']['r']);

		$oForm = new weeForm('resource', 'edit');
		$oForm->fill($oResource);

		if (isset($aEvent['post'])) {
			$aData = $oForm->filter($aEvent['post']);

			try {
				$oForm->validate($aData);

				$oResource->setFromArray($aData);
				$oResource->update();

				$this->update('replaceContent', '#msg', 'The resource has been successfully edited!');
			} catch (FormValidationException $e) {
				$this->update('replaceContent', '#msg', 'The submitted data is erroneous!');
				$oForm->fillErrors($e);
			}

			$oForm->fill($aData);

			if (array_value($aEvent, 'context') == 'xmlhttprequest')
				$this->update('replace', 'form', $oForm);
		}

		$this->set('form', $oForm);
	}
}

The form could be output as follow in the template:

<html>
<head>
	<title>Example</title>
	<script type="text/javascript" src="<?php echo APP_PATH?>res/jquery/jquery.js"></script>
	<script type="text/javascript" src="<?php echo APP_PATH?>res/jquery/jquery.taconite.js"></script>
	<script type="text/javascript" src="<?php echo APP_PATH?>res/jquery/jquery.form.js"></script>
	<script type="text/javascript">
function ajaxify(){
	$('form').ajaxForm(function(){
		// We need to ajaxify again when our form gets replaced
		ajaxify();
	});
}

$(function(){
	// The following function will be called when the document is loaded
	ajaxify();
});
	</script>
</head>

<body>
	<p id="msg">Please edit the resource and submit the form.</p>
	<?php echo $form->toString()?> 
</body>
</html>

The header of the template is slightly different than before. We use jQuery's form plugin to process the form through AJAX. To do that we include an additional javascript file and initialize the plugin. No additional operations required.

Every form submissions will be done using AJAX. The jQuery taconite plugin will detect Taconite responses and act automatically, replacing the form and the message with the information sent by the server.

If the user doesn't have javascript support, the operations will be done server-side and the complete page will be returned modified accordingly.

Limitations

There exists no real limitations client-side. You can pretty much use any transformation command you want. You can even use custom commands, so you would be able to send commands to start visual effects if you wanted to. Read the documentation about extending the taconite plugin for more details.

At the time of the redaction of this documentation, the server-side taconite processor suffers a few limitations compared to its javaScript client-side counterpart:

  • Only #id, //xpath and tags selectors are supported.

  • Multiple selectors cannot be specified using the comma operator.

  • Only a subset of the transformations commands of jQuery is supported.

The supported transformation commands for server-side processing are listed in the following table.

Table 5.3. Server-side taconite supported commands

CommandSummary
afterInsert content after each of the matched elements.
appendAppend content to the inside of every matched element.
beforeInsert content before each of the matched elements.
prependPrepend content to the inside of every matched element.
removeRemove all matched elements from the DOM.
replaceReplace all matched elements with the given content.
replaceContentReplace content of every matched element with the given content.


Feel free to submit patches to support new selectors and/or new commands!