zone
parameter and not async="true"
.
<!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_4.xsd">
<body class="container">
<h3>AJAX Loading Spinner</h3>
<noscript class="js-required">
${message:javascript_required}
</noscript>
This example displays an "AJAX loading spinner" while a Zone request is in progress.
<div class="eg">
<t:form zone="thingsZone" class="form-inline">
<t:eventlink event="showThings" zone="thingsZone">Show things, with a delay...</t:eventlink>
<t:submit t:id="WithAZone" value="Show things, with a delay..." class="btn btn-default"/>
</t:form>
<t:zone t:id="thingsZone" id="thingsZone" style="width: 140px; min-height: 80px;">
<div t:type="Loop" t:source="things" t:value="thing">
${thing}
</div>
</t:zone>
</div>
It uses JavaScript to detect any Form or link request that involves a Zone refresh,
then it overlays the nominated Zone with a CSS class that darkens the area and displays an animated GIF.
When the Zone refresh returns from the server it replaces the Zone completely, without the overlay.<br/><br/>
This solution will work with a Form or link that specifies the <code>zone</code> parameter and not <code>async="true"</code>.<br/><br/>
This solution has additional limitations which are described in the original article
<a href="http://java.dzone.com/articles/adding-ajax-throbbers-zone">Adding 'Ajax Throbbers' to Zone updates</a>.<br/><br/>
For a mixin, have a look at the ZoneLoadingSpinner in Lenny's <a href="http://code.google.com/p/flowlogix/">FlowLogix</a>.<br/><br/>
References:
<a href="http://java.dzone.com/articles/adding-ajax-throbbers-zone">Adding 'Ajax Throbbers'</a>,
<a href="http://tapestry.apache.org/javascript.html">Tapestry JavaScript</a>,
<a href="http://www.w3schools.com/css/css3_animations.asp">CSS3 Animations</a>,
<a href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/annotations/Import.html">@Import</a>,
<a href="http://tapestry.apache.org/5.4/apidocs/org/apache/tapestry5/services/javascript/JavaScriptSupport.html">JavaScriptSupport</a>,
<a href="http://tapestry.apache.org/5.4/coffeescript/dom.html">t5/core/dom</a>,
<a href="http://tapestry.apache.org/5.4/coffeescript/events.html">t5/core/events</a>,
<a href="http://tapestry.apache.org/5.4/coffeescript/zone.html">t5/core/zone</a>,
<a href="http://www.jquery4u.com/tools/online-loading-ajax-spinner-generators/">5 Online Loading AJAX Spinner Generator Tools</a>.<br/><br/>
<t:pagelink page="Index">Home</t:pagelink><br/><br/>
<t:tabgroup>
<t:sourcecodetab src="/web/src/main/java/jumpstart/web/pages/examples/ajax/AjaxLoadingSpinner.tml"/>
<t:sourcecodetab src="/web/src/main/java/jumpstart/web/pages/examples/ajax/AjaxLoadingSpinner.java"/>
<t:sourcecodetab src="/web/src/main/resources/META-INF/modules/zone-overlay.js"/>
<t:sourcecodetab src="/web/src/main/resources/META-INF/assets/css/examples/js.css"/>
<t:sourcecodetab src="/web/src/main/resources/META-INF/assets/css/examples/zone-overlay.css"/>
<t:sourcecodetab src="/web/src/main/resources/META-INF/assets/css/examples/zone-overlay-ie.css"/>
</t:tabgroup>
</body>
</html>
package jumpstart.web.pages.examples.ajax;
import org.apache.tapestry5.Asset;
import org.apache.tapestry5.annotations.Import;
import org.apache.tapestry5.annotations.InjectComponent;
import org.apache.tapestry5.annotations.Path;
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;
import org.apache.tapestry5.services.javascript.JavaScriptSupport;
import org.apache.tapestry5.services.javascript.StylesheetLink;
import org.apache.tapestry5.services.javascript.StylesheetOptions;
@Import(stylesheet = { "css/examples/js.css", "css/examples/zone-overlay.css" })
public class AjaxLoadingSpinner {
static final private String[] ALL_THINGS = { "Sugar", "Spice", "All Things Nice" };
// Screen fields
@Property
private String[] things;
@Property
private String thing;
// Generally useful bits and pieces
@Inject
private Request request;
@Inject
private AjaxResponseRenderer ajaxResponseRenderer;
@InjectComponent
private Zone thingsZone;
@Inject
private JavaScriptSupport javaScriptSupport;
@Inject
@Path("css/examples/zone-overlay-ie.css")
private Asset ieCSS;
// The code
void afterRender() {
// Add a stylesheet that is conditional on whether the browser is Internet Explorer. We would do this in the
// @Import above except that it doesn't have the syntax to specify a condition.
javaScriptSupport.importStylesheet(new StylesheetLink(ieCSS, new StylesheetOptions().withCondition("IE")));
javaScriptSupport.require("zone-overlay");
}
void onShowThings() {
refreshZoneSlowly();
}
void onSuccess() {
refreshZoneSlowly();
}
private void refreshZoneSlowly() {
// Set up the list of things to display
things = ALL_THINGS;
// Sleep 4 seconds to simulate a long-running operation
sleep(4000);
if (request.isXHR()) {
ajaxResponseRenderer.addRender(thingsZone);
}
}
private void sleep(long millis) {
try {
Thread.sleep(millis);
}
catch (InterruptedException e) {
// Ignore
}
}
}
// A script that detects when a zone-related refresh is requested.
// It reacts by overlaying the zone with a div of class "zone-loading-overlay".
// The idea is that you should define that class, in css, to display an animated GIF.
//
// Based on a solution by Howard Lewis Ship at http://tapestryjava.blogspot.co.uk/2011/12/adding-ajax-throbbers-to-zone-updates.html .
define(["t5/core/dom", "t5/core/events", "t5/core/zone"], function(dom, events, zoneManager) {
return function(params) {
function addZoneOverlay() {
var $zone = this.$;
this.prepend("<div class='zone-loading-overlay'/>");
var $overlay = $zone.find("div:first");
$overlay.css({
width : $zone.width() + "px",
height : $zone.height() + "px"
});
}
// Tell t5/core/dom to call my addZoneOverlay() whenever it receives t5/core/events.zone.refresh
// (which bubbles up from t5/core/zone when you click a Form or link that specifies zone).
dom.onDocument(events.zone.refresh, addZoneOverlay);
}
});
.eg {
margin: 20px 0;
padding: 14px;
color: #888;
border: 1px solid #ddd;
border-radius: 6px;
-webkit-border-radius: 6px;
-mox-border-radius: 6px;
}
.js-required {
color: red;
display: block;
margin-bottom: 14px;
}
.js-recommended {
color: red;
display: block;
margin-bottom: 14px;
}
/* Based on a solution by Howard Lewis Ship at http://tapestryjava.blogspot.co.uk/2011/12/adding-ajax-throbbers-to-zone-updates.html .*/
@-webkit-keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: .5
}
}
@-moz-keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: .5
}
}
div.zone-loading-overlay {
position: absolute;
background-color: #eee;
opacity: 0;
-webkit-animation-name: fade-in;
-webkit-animation-duration: 250ms;
-webkit-animation-delay: 50ms;
-webkit-animation-fill-mode: forwards;
-moz-animation-name: fade-in;
-moz-animation-duration: 250ms;
-moz-animation-delay: 50ms;
-moz-animation-fill-mode: forwards;
background-image: url(../../tapestry5/ajax-loader.gif);
background-repeat: no-repeat;
background-position: center center;
z-index: 9999;
}
/* Based on a solution by Howard Lewis Ship at http://tapestryjava.blogspot.co.uk/2011/12/adding-ajax-throbbers-to-zone-updates.html .*/
div.zone-ajax-overlay {
background-color: silver;
filter: alpha(opacity = 50);
}