<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!-- We need a doctype to allow us to use special characters like
We use a "strict" DTD to make IE follow the alignment rules. -->
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
<head>
<link rel="stylesheet" type="text/css" href="${context:css/examples/js.css}"/>
</head>
<body>
<h1>AJAX Multiple Zone Update</h1>
<noscript class="js-required">
${message:javascript_required}
</noscript>
This page demonstrates how multiple zones can be updated simultaneously by using the AjaxResponseRenderer service.
<div class="eg">
<form t:type="form">
<t:zone t:id="nameZone1" id="nameZone1">
Welcome ${name}.
</t:zone><br/>
First Name:
<input t:type="TextField" t:id="firstName" t:mixins="zoneUpdater" t:clientEvent="keyup" t:event="firstNameChanged" t:zone="nameZone1"/><br/>
Last Name:
<input t:type="TextField" t:id="lastName" t:mixins="zoneUpdater" t:clientEvent="keyup" t:event="lastNameChanged" t:zone="nameZone1"/><br/><br/>
<t:zone t:id="nameZone2" id="nameZone2">
Welcome ${upperCaseName}.
</t:zone><br/>
Note that the following time field does not update because it's not in the zone: ${time}<br/>
</form>
</div>
References:
<a href="http://tapestry.apache.org/ajax-and-zones.html">Ajax and Zones</a>,
<a href="http://tapestry.apache.org/5.3/apidocs/org/apache/tapestry5/corelib/components/Zone.html">Zone</a>,
<a href="http://tapestry.apache.org/5.3/apidocs/org/apache/tapestry5/services/ajax/AjaxResponseRenderer.html">AjaxResponseRenderer</a>,
<a href="http://tinybits.blogspot.com/2010/03/new-and-better-zoneupdater.html">Inge's Zone Updater</a>,
<a href="http://tapestry.apache.org/5.3/apidocs/org/apache/tapestry5/services/Request.html">Request</a>,
<a href="http://tapestry.apache.org/5.3/apidocs/org/apache/tapestry5/ComponentResources.html">ComponentResources</a>,
<a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/InjectComponent.html">@InjectComponent</a>,
<a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/Inject.html">@Inject</a>.<br/><br/>
<a t:type="eventlink" t:event="gohome" href="#">Home</a><br/><br/>
<t:sourcecodedisplay src="/web/src/main/java/jumpstart/web/pages/examples/ajax/AjaxMultipleZoneUpdate.tml"/>
<t:sourcecodedisplay src="/web/src/main/java/jumpstart/web/pages/examples/ajax/AjaxMultipleZoneUpdate.java"/>
<t:sourcecodedisplay src="/web/src/main/java/jumpstart/web/css/examples/js.css"/>
<t:sourcecodedisplay src="/web/src/main/java/jumpstart/web/mixins/ZoneUpdater.java"/>
<t:sourcecodedisplay src="/web/src/main/java/jumpstart/web/mixins/ZoneUpdater.js"/>
</body>
</html>
package jumpstart.web.pages.examples.ajax;
import java.util.Date;
import jumpstart.web.pages.Index;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.annotations.InjectComponent;
import org.apache.tapestry5.annotations.Persist;
import org.apache.tapestry5.annotations.Property;
import org.apache.tapestry5.corelib.components.Zone;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.services.Request;
import org.apache.tapestry5.services.ajax.AjaxResponseRenderer;
public class AjaxMultipleZoneUpdate {
// Screen fields
@Property
@Persist
private String firstName;
@Property
@Persist
private String lastName;
// Generally useful bits and pieces
@InjectComponent
private Zone nameZone1;
@InjectComponent
private Zone nameZone2;
@Inject
private Request request;
@Inject
private AjaxResponseRenderer ajaxResponseRenderer;
@Inject
private ComponentResources componentResources;
// The code
// Life-cycle stuff. Fields that are marked @Persist MUST be initialized here rather than where they are declared.
void setupRender() {
if (firstName == null && lastName == null) {
firstName = "Humpty";
lastName = "Dumpty";
}
}
void onFirstNameChanged() {
firstName = request.getParameter("param");
if (firstName == null) {
firstName = "";
}
if (request.isXHR()) {
ajaxResponseRenderer.addRender(nameZone1).addRender(nameZone2);
}
}
void onLastNameChanged() {
lastName = request.getParameter("param");
if (lastName == null) {
lastName = "";
}
if (request.isXHR()) {
ajaxResponseRenderer.addRender(nameZone1).addRender(nameZone2);
}
}
public String getName() {
return firstName + " " + lastName;
}
public String getUpperCaseName() {
return getName().toUpperCase();
}
public Date getTime() {
return new Date();
}
Object onGoHome() {
componentResources.discardPersistentFieldChanges();
return Index.class;
}
}
body { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 13px; font-weight: normal; color: #333;
line-height: 17px; }
h1 { font-size: 26px; line-height: 20px; } /* For IE 7 */
form { margin: 0; }
.eg { margin: 20px 0; padding: 20px; color: #888;
border: 1px solid #ddd; border-radius: 4px; -webkit-border-radius: 4px; -mox-border-radius: 4px; }
a { text-decoration: none; color: #3D69B6; }
a:hover { text-decoration: underline; }
/* For javascript examples. */
.js-required { color: red; display: block; margin-bottom: 14px; }
.js-recommended { color: red; display: block; margin-bottom: 14px; }
.grid { border-collapse: collapse; border-spacing: 0; border: 1px solid #dddddd; font-size: 13px; }
.grid tr.odd { background-color: #f8f8f8; }
.grid tr:hover { background-color: #eeeeee; }
.grid th { padding: 3px 5px; text-align: left; width: 130px; border: 1px solid #dddddd;
font-weight: normal; background-color: #eeeeee;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fbfbfb', endColorstr='#e4e4e4'); /* for IE */
background: -webkit-gradient(linear, left top, left bottom, from(#fbfbfb), to(#e4e4e4)); /* for webkit browsers */
background: -moz-linear-gradient(top, #fbfbfb, #e4e4e4); /* for firefox 3.6+ */ }
.grid td { padding: 3px 5px; text-align:left; }
/**
* A simple mixin for attaching javascript that updates a zone on any client-side event.
* Based on http://tinybits.blogspot.com/2010/03/new-and-better-zoneupdater.html
*/
package jumpstart.web.mixins;
import org.apache.tapestry5.BindingConstants;
import org.apache.tapestry5.ClientElement;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.annotations.Environmental;
import org.apache.tapestry5.annotations.Import;
import org.apache.tapestry5.annotations.InjectContainer;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.json.JSONObject;
import org.apache.tapestry5.services.javascript.JavaScriptSupport;
// The @Import tells Tapestry to put a link to the file in the head of the page so that the browser will pull it in.
@Import(library = "ZoneUpdater.js")
public class ZoneUpdater {
// Parameters
/**
* The event to listen for on the client. If not specified, zone update can only be triggered manually through
* calling updateZone on the JS object.
*/
@Parameter(name = "clientEvent", defaultPrefix = BindingConstants.LITERAL)
private String clientEvent;
/**
* The event to listen for in your component class
*/
@Parameter(name = "event", defaultPrefix = BindingConstants.LITERAL, required = true)
private String event;
@Parameter(name = "prefix", defaultPrefix = BindingConstants.LITERAL, value = "default")
private String prefix;
@Parameter(name = "context")
private Object[] context;
/**
* The zone to be updated by us.
*/
@Parameter(name = "zone", defaultPrefix = BindingConstants.LITERAL, required = true)
private String zone;
/**
* Set secure to true if https is being used, else set to false.
*/
@Parameter(name = "secure", defaultPrefix = BindingConstants.LITERAL, value = "false")
private boolean secure;
// Useful bits and pieces
@Inject
private ComponentResources componentResources;
@Environmental
private JavaScriptSupport javaScriptSupport;
/**
* The element we attach ourselves to
*/
@InjectContainer
private ClientElement clientElement;
// The code
void afterRender() {
String listenerURI = componentResources.createEventLink(event, context).toAbsoluteURI(secure);
// Add some JavaScript to the page to instantiate a ZoneUpdater. It will run when the DOM has been fully loaded.
JSONObject spec = new JSONObject();
spec.put("elementId", clientElement.getClientId());
spec.put("clientEvent", clientEvent);
spec.put("listenerURI", listenerURI);
spec.put("zoneId", zone);
javaScriptSupport.addScript("%sZoneUpdater = new ZoneUpdater(%s)", prefix, spec.toString());
}
}
// A class that updates a zone on any client-side event.
// Based on http://tinybits.blogspot.com/2010/03/new-and-better-zoneupdater.html
// and some help from Inge Solvoll.
ZoneUpdater = Class.create( {
initialize : function(spec) {
this.element = $(spec.elementId);
this.listenerURI = spec.listenerURI;
$(this.element).getStorage().zoneId = spec.zoneId;
if (spec.clientEvent) {
this.clientEvent = spec.clientEvent;
this.element.observe(this.clientEvent, this.updateZone.bindAsEventListener(this));
}
},
updateZone : function() {
var zoneManager = Tapestry.findZoneManager(this.element);
if (!zoneManager) {
return;
}
var listenerURIWithValue = this.listenerURI;
if (this.element.value) {
var param = this.element.value;
if (param) {
listenerURIWithValue = addQueryStringParameter(listenerURIWithValue, 'param', param);
}
}
zoneManager.updateFromURL(listenerURIWithValue);
}
} )
function addQueryStringParameter(url, name, value) {
if (url.indexOf('?') < 0) {
url += '?'
} else {
url += '&';
}
value = escape(value);
url += name + '=' + value;
return url;
}