...
That onUpdateTime
method is just an ordinary Tapestry event handler, except that it uses an injected AjaxResponseRenderer
to tell Tapestry what zone to update when the link is clicked.
Since Tapestry 5.4.2, you can also easily invoke server-side event handlers using the @PublishEvents
annotation and the t5/core/ajax
JavaScript function, as explained in the Invoking server-side event handler methods from JavaScript section below.
Zones
Zones are Tapestry's approach to performing partial page updates. A Zone component renders as an HTML element, typically a <div>, and serves as a marker for where dynamically-updated content should be replaced. A zone is recognizable in the DOM because it will have the attribute data-container-type=zone
. The client-side support for Zones is keyed off of this attribute and value.
Starting in Tapestry 5.4 you can use any HTML element in your template as a zone marker, by passing its client-side id to the two-argument version of the addRender method.
A Zone updated can be triggered by an EventLink, ActionLink or Select component, or by a Form. All of these components support the async
and/or zone
parameters. Clicking such a link will invoke an event handler method on the server as normal ... except that a partial page response is sent to the client, and the content of that response is used to update the Zone's <div> in place.
Wiki Markup |
---|
{float:right|background=#eee|padding=0 1em}
*JumpStart Demo:*
[AJAX ActionLink| |
...
https://tapestry-jumpstart. |
...
org/jumpstart/examples/ajax/actionlink] {float} |
Event Handler Return Types
...
- An injected Block or Component to render as the response. The response will be a JSON hash, with a "content" key whose value is the rendered markup. This is the basis for updates with the Zone component.
- The zone's own body (using Zone's getBody() method)
- null (to redraw the current page)
- A JSONObject or JSONArray, which will be sent as the response.
- A StreamResponse, which will be sent as the response.
- A Link, which will send a redirect to the client.
- A page name (as a String), or a page class, or a page instance, which will send a redirect to the indicated page.
See See Page Navigation for full descriptions of the above.
...
Code Block | ||
---|---|---|
| ||
@Inject private Request request; @InjectComponent private Zone myZone; ... Object onActionFromSomeLink() { // return either the zone body (ajax) or whole page (non-ajax) return request.isXHR() ? myZone.getBody() : null; } |
Multiple Zone Updates
Wiki Markup |
---|
{float:right|background=#eee|padding=0 1em}
*JumpStart Demo:*
[AJAX Multiple Zone Update| |
...
https://tapestry-jumpstart. |
...
org/jumpstart/examples/ajax/multiplezoneupdate] {float} |
An event handler often needs to update multiple zones on the client side. To accomplish this, use an AjaxResponseRenderer, indicating the zones to update. You must know the client-side id for each zone to update (the best way for this is to lock down the zone's id using the id parameter of the Zone component).
...
Code Block | ||||
---|---|---|---|---|
| ||||
@InjectComponent private Zone userInput; @InjectComponent private Zone helpPanel; @Inject private AjaxResponseRenderer ajaxResponseRenderer; void onActionFromRegister() { ajaxResponseRenderer.addRender("userInput", userInput).addRender("helpPanel", helpPanel); } |
This example assumes that there are two zones, "userInput" and "helpPanel", somewhere in the rendered page, waiting to receive the updated content.
Note |
---|
In this example, the Zone receives the update but does not provide any content. That's OK, the other client-side elements ( |
...
Remember that the component id (t:id
) is used to inject the Zone component into the containing page or component. The
client-side id (id
) is used on the client side to orchestrate requests and updates. You will often seen the following construct:
Code Block | ||
---|---|---|
| ||
<t:zone t:id="myZone" id="myzone"> ... </t:zone>
<t:actionlink t:id="update" zone="myzone">update</t:actionlink>
|
The Containing Zone (zone="^")
...
Since 5.2
If the Form or Link is enclosed by the Zone itself,
...
and you're using the zone
parameter instead of the async
parameter, then the zone
parameter may be set to the special
...
value ^
(the carat)
. The
...
zone is
...
found – on the client
...
side – by searching up form the form or link element for the first enclosing element that is a Zone. In this way, the client-side coordination can occur without having to know what the specific client-side id of the Zone is. Because of this, in some cases
...
it is no longer necessary to specify the Zone'
...
s id
...
parameter.
An Update div within a Zone div (Tapestry 5.3 and earlier)
...
Deprecated : This feature is removed starting with Tapestry 5.4
...
In many situations, a Zone is a kind of "wrapper" or "container" for dynamic content; one that provides a look and feel ... a bit of wrapping markup to create a border. In that situation, the Zone <div> may contain an update <div>.
...
Zone Effect Functions (Tapestry 5.3 and earlier)
...
Deprecated: This feature refers to client-side logic only present in Tapestry 5.3 or earlier. For 5.4, there are client-side events that are triggered before and after changes to the Zone; listeners on those events can trigger whatever animations they like.
A Zone may be initially visible or invisible. When a Zone is updated, it is made visible if not currently so. This is accomplished via a function on the Tapestry.ElementEffect client-side object. By default, the show() function is used for this purpose. If you want Tapestry to call a different Tapestry.ElementEffect function when updates occur, specify its name with the zone's show parameter.
If a Zone is already visible, then a different effect function is used to highlight the change. By default, the highlight() function is called, which performs a yellow fade to highlight that the content of the Zone has changed. Alternatively, you can specify a different effect function with the Zone's update parameter:
Tapestry.ElementEffect Function | Result |
---|---|
highlight() | (the default) highlight changes to an already-visible zone |
show() | make the zone visible if it isn't already visible |
slidedown() | scroll the content down |
slideup() | slide the content back up (opposite of slidedown) |
fade() | fade the content out (opposite of show) |
To have Tapestry update a zone without the usual yellow highlight effect, just specify "show" for the update parameter:
...
Unlike many other situations, Tapestry relies on you to specify useful and unique ids to Zone components, then reference those ids inside EventLink (or ActionLink, or Form) components. Using Zone components inside any kind of loop may cause additional problems, as Tapestry will uniqueify the client id you specify (appending an index number).
The show and update function names are converted to lower case; all the methods of Tapestry.ElementEffect should have all lower-case names. Because client-side JavaScript is so fluid (new methods may be added to existing objects), Tapestry makes no attempt to validate the function names ... however, if the names are not valid, then the default show and highlight methods will be used.
Zones may only be used inside the body of a page, not the head.
More Information
If you create a component that contains a zone, and you use that component in a loop, you'll likely need to set the client-side id like this:
Code Block | ||
---|---|---|
| ||
<t:zone t:id="myzone" id="prop:componentResources.id"> |
See this JumpStart Example for details.
The show and update function names (Tapestry 5.3 and earlier only) are converted to lower case; all the methods of Tapestry.ElementEffect should have all lower-case names. Because client-side JavaScript is so fluid (new methods may be added to existing objects), Tapestry makes no attempt to validate the function names ... however, if the names are not valid, then the default show and highlight methods will be used.
Zones may only be used inside the body of a page, not the head.
More Information
For For examples of extending a Form with a Zone and updating multiple zones at once, see the Ajax Components FAQ.
There are also a number of Ajax-related examples at the Tapestry JumpStart site.
Anchor | ||||
---|---|---|---|---|
|
Autocomplete Mixin
Wiki Markup |
---|
{float:right|background=#eee|padding=0 1em}
*JumpStart Demo:*
[Autocomplete Mixin| |
...
https://tapestry-jumpstart. |
...
org/jumpstart/examples/ajax/autocompletemixin] {float} |
The Autocomplete mixin exists to allow a text field to query the server for completions for a partially entered phrase. It is often used in situations where the field exists to select a single value from a large set, too large to successfully download to the client as a drop down list; for example, when the number of values to select from is numbered in the thousands.
...
The mixin can be configured in a number of ways, see the component reference.
When the user types into the field, the client-side JavaScript will send a request to the server to get completions.
...
You can return an object array, a list, even a single object. You may return objects instead of strings ... and toString()
will be used to convert them into client-side strings.
Anchor | ||||
---|---|---|---|---|
|
Invoking server-side event handler methods from JavaScript
Tapestry 5.4.2 introduced an API which makes it easy for server-side events to be invoked from JavaScript. On the server-side, you first need to annotate the event handler methods you want to expose with the @PublishEvent
annotation. Then, in JavaScript, all you need to do is to call the existing t5/core/ajax
function, but with slightly different parameters.
The t5/core/ajax
function has two parameters: url
and options
. Prior to Tapestry 5.4.2, the first one was difficult to get when doing AJAX requests to event handler methods. You needed to inject ComponentResources
in your component class, call componentResources.createEventLink()
for each event handler method, then pass all this information back to the browser through one of the JavaScriptSupport
methods. For Tapestry 5.4.2 and later, your JavaScript code only needs to know the event name (also called event type) and optionally indicate a DOM element to be used as a starting point for finding the event URL.
All event data is stored in data-componenent-events
attributes. For page classes, the attribute is added to the <body>
element. For components, it's added to the first element rendered by the component. Given an HTML element, the search is performed in the following order until information for the given event is first found:
- The element itself
- The element's previous siblings, closest first (bottom-up)
- The element's parents
- The page's
<body>
element
Here's one example:
Code Block | ||
---|---|---|
| ||
public class PublishEventDemoComponent
{
@OnEvent("answer")
@PublishEvent
JSONObject answer() {
return new JSONObject("origin", "componentAnswer");
}
@PublishEvent
JSONObject onAction()
{
return new JSONObject("origin", "componentAction");
}
}
|
Notice that answer()
and onAction()
are ordinary event handlers, with nothing specific besides the @PublishEvent
annotation.
Code Block | ||
---|---|---|
| ||
<div id="component" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
<p id="componentParagraph">I'm a component</p>
<p id="result">(no result yet)</p>
</div> |
The template also has nothing special. When rendered, the component's events information is placed in the outer <div id="component">
.
We want to update the text of <p id="result">
with the value of the origin
property of the returned JSON object when that element itself is clicked, so here's our JavaScript code, supposing we want to trigger the answer
event:
Code Block | ||||
---|---|---|---|---|
| ||||
require(["t5/core/ajax", "jquery"], function (ajax, $) {
// Creating a callback to be invoked with <p id="result"> is clicked.
$('#result').click(function() {
ajax('answer', {
element: $('#result'), // This doesn't need to be the same element as the one two lines above
// Callback called when the request is finished.
// response.json is the object returned by the event handler method
success: function(response) {
$('#result').text(response.json.origin);
}
});
});
}); |
If you're trying to invoke a page class event handler, you can change line 5 above to element: null
. You do need to explicitly set the element
property, otherwise the ajax
function will treat the first parameter, url
, as an URL and not as an event name.