Hi!

This is something I've wanted for a really long time to implement in
Tapestry, so I won't wait for it to be released or even the documentation
to be updated in the site to post it here. Feedback welcome!
Invoking server-side event handler methods from JavaScript

Tapestry 5.4.2 introduced has an API which makes it easy for server-side
events to be invoked from JavaScript. In the server-side, you first need to
annotate the event handler methods you want exposed with the new
@PublishEvent annotation. Then, in JavaScript, all you need to do is to
call the existing t5/core/ajax
<http://tapestry.apache.org/current/coffeescript/ajax.html> function but
with slightly different parameters.

t5/core/ajax has two parameters: url and options. The first one was the
difficult part to get when doing AJAX requests to event handler methods in
Tapestry. You needed to inject ComponentResources, call
componentResources.createEventLink() for each event handler method then
pass all this information to JS through one of the JavaScriptSupport methods.
Since 5.4.2, 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, it's put in the <body> element. For components, it's put in the
first element rendered created by rendering the component. Given an HTML
element, the search is done until one in this order until information for
the given event is first found:

   1. The element itself
   2. The element's previous siblings, closest first (bottom-up)
   3. The element's parents.
   4. The page's <body> element.



Here's one example:
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.
<div id="component" xmlns:t="
http://tapestry.apache.org/schema/tapestry_5_0_0.xsd";
<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:
1
2
3
4
5
6
7
8
9
10
11
12
13
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.

-- 
Thiago

Reply via email to