blob: b176d0dbc230dbe64b4516d06c8f35f301691ae0 [file] [log] [blame]
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<chapter id="chapter-ajax" remap="h1">
<title>Ajax</title>
<sect1 id="ajax-overview" remap="h2">
<title>Ajax Overview</title>
<para>Ajax is a method of using JavaScript to perform a GET or POST request
and return a result without reloading the whole page.
</para>
<para>For an introduction on Ajax please see the following articles:</para>
<itemizedlist>
<listitem>
<para><ulink url="http://www.w3schools.com/Ajax/default.asp">http://www.w3schools.com/Ajax/default.asp</ulink></para>
</listitem>
<listitem>
<para><ulink url="http://en.wikipedia.org/wiki/AJAX">http://en.wikipedia.org/wiki/AJAX</ulink></para>
</listitem>
</itemizedlist>
<para>Ajax is a client-side technology for creating interactive web applications.
The JavaScript <literal>XMLHttpRequest</literal> object is used to perform GET
and POST requests and the server can send back a response that can be processed
in the browser.
</para>
<para>Click on the other hand is a server-side technology that can handle and
process incoming Ajax requests and send a response back to the browser.
</para>
<para><emphasis role="bold">Please note:</emphasis> Click is responsible
for handling server-side requests. It is up to you to develop the client-side
logic necessary to make the Ajax request, process the server response
and handle errors. This is easier than it sounds though as there is a wide
range of free JavaScript libraries and plugins available to help develop
rich HTML front-ends. Since Click is a stateless framework and Page URLs
are bookmarkable, it is easy to integrate Click with existing JavaScript
technologies such as:
<ulink url="http://www.jquery.com">jQuery</ulink>,
<ulink url="http://www.prototypejs.org/">Prototype</ulink> and
<ulink url="http://mootools.net/">MooTools</ulink> to name a few.
</para>
<para>It is also possible to write custom
<ulink url="../../click-api/org/apache/click/ajax/AjaxBehavior.html">AjaxBehaviors</ulink>
(covered later) that renders the client-side code necessary to initiate Ajax
requests and handle Ajax responses and errors. In fact once you become familiar
Click's Ajax handling, you will likely create custom AjaxBehaviors to
streamline and encapsulate your client-side code.
</para>
<para>In this chapter we'll look at the Ajax support provided by Click. There
are two basic ways to handle and process Ajax requests:
</para>
<itemizedlist>
<listitem>
<para><link linkend="ajax-behavior">AjaxBehavior</link> - AjaxBehavior
is a specialized
<ulink url="../../click-api/org/apache/click/Behavior.html">Behavior</ulink>
that enables Controls to handle and respond to Ajax requests
</para>
</listitem>
<listitem>
<para><link linkend="ajax-page-action">Page Actions</link> - Page Actions
was covered <link linkend="page-actions">earlier</link> and provides a
simple way to handle and respond to Ajax requests
</para>
</listitem>
</itemizedlist>
</sect1>
<sect1 id="ajax-behavior" remap="h2">
<title>AjaxBehavior</title>
<para><ulink url="ajax-behavior">AjaxBehavior</ulink> is an interface that
extends Behavior (covered <link linkend="behavior">earlier</link>) and
adds the ability to handle and process incoming Ajax requests.
</para>
<para>Click also provides a default AjaxBehavior implementation,
<ulink url="../../click-api/org/apache/click/ajax/DefaultAjaxBehavior.html">DefaultAjaxBehavior</ulink>.
Using this class you only need to implement the methods you are interested in.
</para>
<para>AjaxBehaviors, like Behaviors, are added to controls
through the <ulink url="../../click-api/org/apache/click/control/AbstractControl.html#addBehavior(org.apache.click.Behavior)">AbstractControl.addBehavior()</ulink>
method.
</para>
<para>AjaxBehaviors provides an
<ulink url="../../click-api/org/apache/click/ajax/AjaxBehavior.html#onAction(org.apache.click.Control)">onAction</ulink>
method (similar to <classname>ActionListener</classname>) that is invoked to
handle Ajax requests. The <methodname>onAction</methodname>
method returns an <ulink url="../../click-api/org/apache/click/ActionResult.html">ActionResult</ulink>
containing the data to be rendered to the browser.
</para>
<para>The <classname>Control</classname>, <classname>Behavior</classname>,
<classname>AjaxBehavior</classname> and <classname>ActionResult</classname>
classes are depicted in the figure below.
</para>
<figure id="ajax-behavior-class-diagram">
<title>Ajax Behavior Class Diagram</title>
<inlinemediaobject>
<imageobject>
<imagedata fileref="images/ajax/ajax-class-diagram.png" format="PNG" scale="65"/>
</imageobject>
</inlinemediaobject>
</figure>
<para>The following method is exposed by Control in order to handle Ajax
requests:
</para>
<itemizedlist>
<listitem>
<para>
<ulink url="../../click-api/org/apache/click/Control.html#isAjaxTarget(org.apache.click.Context)">isAjaxTarget(Context)</ulink>
- Returns true if the control is the Ajax request <literal>target</literal>,
false otherwise. The <literal>Ajax target control</literal> is the Control
which <methodname>onProcess</methodname> method will be invoked. Other
controls won't be processed. The most common way to target a specific
server side control is to give it an
<ulink url="../../click-api/org/apache/click/control/AbstractControl.html#setId(java.lang.String)">HTML ID</ulink>
attribute, which is then passed as an Ajax request parameter to the server.
More on this later.
</para>
</listitem>
</itemizedlist>
<para>The Behavior interface has been covered <link linkend="behavior-class-diagram">already</link>
so we'll look at AjaxBehavior next:
</para>
<itemizedlist>
<listitem>
<para>
<ulink url="../../click-api/org/apache/click/Behavior.html#isAjaxTarget(org.apache.click.Context)">isAjaxTarget(Context)</ulink>
- determines whether the AjaxBehavior is the request target or not. Click
will only invoke the AjaxBehavior <methodname>onAction</methodname> method
if <methodname>isAjaxTarget</methodname> returns true. This
allows for fine grained control over the execution of the <methodname>onAction</methodname>
method.
</para>
</listitem>
<listitem>
<para>
<ulink url="../../click-api/org/apache/click/ajax/AjaxBehavior.html#onAction(org.apache.click.Control)">onAction(Control)</ulink>
- the AjaxBehavior action method for handling Ajax requests.
</para>
</listitem>
</itemizedlist>
<para>The <methodname>onAction</methodname> method returns an
<classname>ActionResult</classname> instance, containing the data to be
rendered to the browser. ActionResult can return any type of response: String,
byte array or a Velocity (Freemarker) template.
</para>
<para>The <methodname>isAjaxTarget</methodname> method controls whether or
not the <methodname>onAction</methodname> method should be invoked.
<methodname>isAjaxTarget()</methodname> is typically used to target the
AjaxBehavior for specific JavaScript events. For example an AjaxBehavior might
only handle <literal>click</literal> or <literal>blur</literal> JavaScript events.
Of course the client-side code initiating the Ajax request should pass
the JavaScript event to the server.
</para>
<para>Lastly the <classname>ActionResult</classname> methods are shown below:
</para>
<itemizedlist>
<listitem>
<para>
<ulink url="../../click-api/org/apache/click/ActionResult.html#setContent(java.lang.String)">setContent(String)</ulink>
- set the String content to render to the browser
</para>
</listitem>
<listitem>
<para>
<ulink url="../../click-api/org/apache/click/ActionResult.html#setBytes(byte[])">setBytes(byte[])</ulink>
- set the byte array to render to the browser
</para>
</listitem>
<listitem>
<para>
<ulink url="../../click-api/org/apache/click/ActionResult.html#setBytes(byte[])">setTemplate(String)</ulink>
- set the name of the Velocity (or Freemarker) template to render to
the browser
</para>
</listitem>
<listitem>
<para>
<ulink url="../../click-api/org/apache/click/ActionResult.html#setModel(java.util.Map)">setModel(Map)</ulink>
- set the Velocity (or Freemarker) template model
</para>
</listitem>
<listitem>
<para>
<ulink url="../../click-api/org/apache/click/ActionResult.html#setContentType(java.lang.String)">setContentType(String)</ulink>
- set the ActionResult content type, for example: <literal>text/html</literal>,
<literal>text/xml</literal>, <literal>application/json</literal> etc.
</para>
</listitem>
</itemizedlist>
</sect1>
<sect1 id="ajax-behavior-execution" remap="h2">
<title>AjaxBehavior Execution</title>
<para>The execution sequence for an <classname>AjaxBehavior</classname>
being processed and rendered is illustrated in the figure below. Note
that it is similar to a normal HTTP request flow. The main differences are that
Ajax requests do not have an onGet or onRender event and that only the
<literal>Ajax target Control</literal> is processed.
</para>
<figure id="ajax-behavior-sequence-diagram">
<title>AjaxBehavior Sequence Diagram</title>
<inlinemediaobject>
<imageobject>
<imagedata fileref="images/ajax/ajax-request-sequence-diagram.png" format="PNG" scale="58"/>
</imageobject>
</inlinemediaobject>
</figure>
<para>Stepping through this Ajax GET request sequence, first a new Page
instance is created.
</para>
<para>Then the <methodname>onSecurityCheck()</methodname> handler is executed
to authorize access to the page, and if necessary abort further processing.
If the request is aborted for an Ajax request, no response is rendered to
the browser. If you want to render a response you need to write to the
<classname>HttpServletResponse</classname> directly or create and
render an <classname>ActionResult</classname> yourself.
</para>
<para>The next method invoked is <methodname>onInit()</methodname> to initialize,
create and setup controls and <classname>Behaviors</classname>.
<methodname>onInit</methodname> is an ideal place to add <classname>Behaviors</classname>
to <classname>Controls</classname>. When a Behavior is added to a Control that
Control is automatically registered with the
<ulink url="../../click-api/org/apache/click/ControlRegistry.html">ControlRegistry</ulink>
as a potential
<ulink url="../../click-api/org/apache/click/ControlRegistry.html#registerAjaxTarget(org.apache.click.Control)">Ajax target control</ulink>.
</para>
<para>The next step is to find and process the <literal>Ajax target control</literal>.
First the ClickServlet needs to determine which Control is the Ajax target.
To resolve the target Control the ClickServlet iterates over all the Controls
registered with the ControlsRegistry and invokes each Control's
<methodname>isAjaxTarget</methodname> method. The first control which
<methodname>isAjaxTarget</methodname> method returns <emphasis>true</emphasis>,
will be the Ajax target.
</para>
<para>The simplest <methodname>isAjaxTarget</methodname> implementation is
to return <emphasis>true</emphasis> if the Control ID is passed as a request
parameter. The client-side JavaScript code that initiate the Ajax request,
must ensure the Control ID is sent as part of the Ajax request. Note, if the
ClickServlet cannot find a target control, no response is rendered.
</para>
<para>If an Ajax target control is found, the ClickServlet will invoke
that control's <methodname>onProcess</methodname> method. Other controls are not
processed.
</para>
<para>
<emphasis role="bold">Please note:</emphasis> since Click is a stateless framework,
processing a control for an Ajax request has the same requirements as processing
a control for a non-Ajax request. In other words, in addition to the Control
ID (or other identifier), the Ajax request must include all the parameters
normally expected by the target Control and its children. For example, a
Field expects it's <literal>name/value</literal> parameter while an ActionLink
expects its <literal>actionLink/name</literal> parameter.
Putting it another way, if for example an ActionLink is clicked and
we only pass the link's HTML ID parameter, Click will identify the link
as the Ajax target control and invoke the link's
<methodname>onProcess</methodname> method. The
<methodname>onProcess</methodname> method is where the link's values are
bound and if it was clicked it's action event (AjaxBehavior) will be fired.
An ActionLink is "clicked" if the <literal>actionLink</literal> parameter
has a value matching the link's name. If no <literal>actionLink</literal>
parameter is present, the server doesn't know that the link was clicked
and won't fire the AjaxBehavior's <methodname>onAction</methodname> event.
So for an Ajax request it is still necessary to pass all the parameters
normally expected by the ActionLink <methodname>onProcess</methodname> method.
For ActionLink that means the Ajax request must include it's
<literal>href</literal> parameters while a Form would require all it's
Field <literal>name/value</literal> pairs.
</para>
<para>Next, the target control <classname>AjaxBehaviors</classname> are
fired. The ClickServlet iterates over the control AjaxBehaviors and for each
AjaxBehavior invoke the method: <methodname>isAjaxTarget</methodname>.
Each AjaxBehavior which <methodname>isAjaxTarget</methodname> method returns
<emphasis>true</emphasis>, will have their <methodname>onAction</methodname>
method invoked to handle the Ajax request. The AjaxBehavior's
<methodname>onAction</methodname> method returns an
<classname>ActionResult</classname> that is rendered to the browser.
</para>
<para>Please note: multiple AjaxBehaviors can handle the same Ajax request,
however only the first <classname>ActionResult</classname> returned will be
rendered to the browser. If an <methodname>onAction</methodname> method
returns <literal>null</literal>, the <classname>ActionResult</classname>
returned by the next AjaxBehavior's onAction method will be used. If all
onAction methods returns null, no response is rendered.
</para>
<para>Next the ActionResult is rendered to the browser.</para>
<para>The final step in this sequence is invoking each control's onDestroy()
method and lastly invoke the Page onDestroy() method.</para>
</sect1>
<sect1 id="first-ajax-example" remap="h2">
<title>First Ajax Example</title>
<para>In this first example we demonstrate how to handle Ajax requests with a
<symbol>DefaultAjaxBehavior</symbol>. DefaultAjaxBehavior is the default
implementation of the <classname>AjaxBehavior</classname> interface. Below is
the Page class, <classname>AjaxBehaviorPage.java</classname>, showing a
<symbol>DefaultAjaxBehavior</symbol> added to an ActionLink, called
<emphasis>link</emphasis> with an HTML ID of <varname>link-id</varname>.
The <symbol>DefaultAjaxBehavior</symbol> <varname>onAction</varname>
method will be invoked to handle the Ajax request. The <varname>onAction</varname>
method returns an <token>ActionResult</token> that is rendered to the browser.
</para>
<programlisting language="java">public class AjaxBehaviorPage extends BorderPage {
private static final long serialVersionUID = 1L;
private ActionLink link = new ActionLink("link", "here");
public AjaxBehaviorPage() {
link.setId("link-id"); <co id="co-link-id" linkends="ca-link-id"/>
addControl(link);
// Add a DefaultAjaxBehavior to the link. The DefaultAjaxBehavior will be invoked when the
// link is clicked.
link.addBehavior(new <symbol>DefaultAjaxBehavior()</symbol> { <co id="co-ajax-behavior" linkends="ca-ajax-behavior"/>
@Override
public <token>ActionResult</token> <varname>onAction</varname>(Control source) { <co id="co-ajax-behavior-method" linkends="ca-ajax-behavior-method"/>
// Formatted date instance that will be added to the
String now = format.currentDate("MMM, yyyy dd HH:mm:ss");
String msg = "AjaxBehavior &lt;tt&gt;onAction()&lt;/tt&gt; method invoked at: " + now;
// Return an action result containing the message
return new <token>ActionResult</token>(msg, ActionResult.HTML); <co id="co-ajax-action-result" linkends="ca-ajax-action-result"/>
}
});
}
} </programlisting>
<calloutlist>
<callout arearefs="co-link-id" id="ca-link-id">
<para>We assign to ActionLink the HTML ID: <varname>link-id</varname>.
The ID will be used to identify the ActionLink as the
<literal>Ajax target control</literal>. The client-side JavaScript code
is expected to send this ID as an Ajax request parameter.
</para>
</callout>
<callout arearefs="co-ajax-behavior" id="ca-ajax-behavior">
<para>Next we add the <symbol>DefaultAjaxBehavior</symbol> to the ActionLink.
Adding a Behavior to a control will also register that control with the
<ulink url="../../click-api/org/apache/click/ControlRegistry.html">ControlRegistry</ulink>
as a potential <literal>Ajax target control</literal>.
</para>
</callout>
<callout arearefs="co-ajax-behavior-method" id="ca-ajax-behavior-method">
<para>We also implement the <symbol>DefaultAjaxBehavior</symbol>
<varname>onAction</varname> method in order to handle the Ajax request.
</para>
</callout>
<callout arearefs="co-ajax-action-result" id="ca-ajax-action-result">
<para>Finally we return an <token>ActionResult</token> containing some
HTML content that is rendered to the browser.
</para>
</callout>
</calloutlist>
<para>Below we show the Page template <literal>ajax-behavior.htm</literal>,
containing the client-side JavaScript code that will initiate the Ajax request.
</para>
<para><emphasis role="bold">Note:</emphasis> the example below uses the
<ulink url="http://www.jquery.com">jQuery</ulink> library, but any other
library can be used. Also see the online Click examples for more Ajax demos.
</para>
<programlisting language="javascript">&lt;!-- // $link is a Velocity reference that will render an ActionLink at runtime. --&gt;
Click $link to make an Ajax request to the server.
&lt;div id="result"&gt;
&lt;!-- // Ajax response will be set here --&gt;
&lt;/div&gt;
&lt;!-- // JavaScript code below --&gt;
&lt;!-- // Import the jQuery library --&gt;
&lt;script type="text/javascript" src="$context/js/jquery.js"&gt;&lt;/script&gt;
&lt;!-- // The client-side JavaScript for initiating an Ajax request --&gt;
&lt;script type="text/javascript"&gt;
// This example uses jQuery for making Ajax requests:
// Register a function that is invoked as soon as the entire DOM has been loaded
jQuery(document).ready(function() { <co id="co-ajax-jq-function" linkends="ca-ajax-jq-function"/>
// Register a 'click' handler that makes an Ajax request
jQuery("#link-id").click(function(event){
// Make ajax request
makeRequest();
// Prevent the default browser behavior of navigating to the link
return false;
})
})
function makeRequest() {
// Get a reference to the link
var link = jQuery('#link-id');
// In order for Click to identify the Ajax target, we pass the link ID
// attribute as request parameters
var extraData = link.attr('id') + '=1'; <co id="co-ajax-link-id" linkends="ca-ajax-link-id"/>
// The Ajax URL is set to the link 'href' URL which contains all the link default parameters,
// including it's name/value pair: 'actionLink=link'
var url = link.attr('href'); <co id="co-ajax-jq-href" linkends="ca-ajax-jq-href"/>
jQuery.get(url, extraData, function(data) {
// 'data' is the response returned by the server
// Find the div element with the id "result", and set its content to the server response
jQuery("#result").html("&lt;p&gt;" + data + "&lt;/p&gt;"); <co id="co-ajax-jq-response" linkends="ca-ajax-jq-response"/>
});
}
&lt;/script&gt; </programlisting>
<calloutlist>
<callout arearefs="co-ajax-jq-function" id="ca-ajax-jq-function">
<para>We start off with a jQuery <ulink url="http://api.jquery.com/ready/">ready</ulink>
function that is executed as soon as the browser DOM has been loaded.
This ensures that the function body is executed <literal>before</literal>
the page images are downloaded, which results in a more responsive UI.
</para>
</callout>
<callout arearefs="co-ajax-link-id" id="ca-ajax-link-id">
<para>This is an important step. We need to pass the link's <varname>HTML ID</varname>
attribute as request parameters in order for the server to identify <literal>which</literal>
server-side control is the <literal>Ajax target</literal>. We use the jQuery
<literal>attr</literal> function to lookup the link's <varname>HTML ID</varname>
attribute. Click doesn't actually use the <literal>value</literal> of the
parameter, it is enough that the <literal>name</literal> is present.
In this example we pass a value of <varname>1</varname>, but any
other value could be used, or even left out.
</para>
</callout>
<callout arearefs="co-ajax-jq-href" id="ca-ajax-jq-href">
<para>This is another important step. In addition to the ActionLink HTML ID
pararameter, we also need to send the link's <varname>href</varname>
parameters, otherwise the ActionLink <methodname>onProcess</methodname>
method won't fire the <classname>AjaxBehavior</classname>
<methodname>onAction</methodname> event. An easy way to achieve this to
set the Ajax <varname>URL</varname> to the ActionLink
<varname>href</varname> value.
</para>
</callout>
<callout arearefs="co-ajax-jq-response" id="ca-ajax-jq-response">
<para>Finally we the jQuery
<ulink url="http://api.jquery.com/html/#html2">html</ulink> function
to update the <emphasis>div</emphasis> content with the server response.
</para>
</callout>
</calloutlist>
<sect2 id="ajax-trace-log" remap="h3">
<title>Ajax Trace Log</title>
<para>Looking at the output log we see the following trace:
</para>
<literallayout>[Click] [debug] GET http://localhost:8080/mycorp/ajax/ajax-behavior.htm
[Click] [trace] <symbol>is Ajax request</symbol>: <varname>true</varname>
[Click] [trace] request param: <varname>actionLink=link</varname>
[Click] [trace] request param: <varname>link-id=1</varname>
[Click] [trace] invoked: AjaxBehaviorPage.&lt;&lt;init&gt;&gt;
[Click] [trace] invoked: AjaxBehaviorPage.onSecurityCheck() : true
[Click] [trace] invoked: AjaxBehaviorPage.onInit()
[Click] [trace] invoked: 'link' ActionLink.onInit()
[Click] [trace] <token>the following controls have been registered as potential Ajax targets:</token>
[Click] [trace] ActionLink: name='link'
[Click] [trace] invoked: 'link' ActionLink.<symbol>isAjaxTarget()</symbol> : <varname>true</varname> (Ajax target control found)
[Click] [trace] invoked: 'link' ActionLink.<symbol>onProcess()</symbol> : <varname>true</varname>
[Click] [trace] <token>processing AjaxBehaviors for control: 'link' ActionLink</token>
[Click] [trace] invoked: AjaxBehaviorPage.1.<symbol>isRequestTarget()</symbol> : <varname>true</varname>
[Click] [trace] invoked: AjaxBehaviorPage.1.<symbol>onAction()</symbol> : <varname>ActionResult</varname> (ActionResult will be rendered)
[Click] [info ] <token>renderActionResult</token> (text/html) - 1 ms
[Click] [trace] invoked: 'link' ActionLink.onDestroy()
[Click] [trace] invoked: AjaxBehaviorPage.onDestroy()
[Click] [info ] handleRequest: /ajax/ajax-behavior.htm - 25 ms</literallayout>
<para>First thing we notice is that the request is recognized as an
<symbol>Ajax request</symbol>.
</para>
<para>We can also see from the log that the Ajax request passed the parameters,
<varname>link-id=1</varname> and <varname>actionLink=link</varname> to the server.
<varname>link-id</varname> is the ActionLink HTML ID attribute that will be used
to identify the Control as the <literal>Ajax request target</literal>.
</para>
<para>Next, the log prints which controls have been registered as <literal>potential
Ajax targets</literal>. In our example we added an AjaxBehavior to the ActionLink
so the ActionLink is registered as an Ajax target.
</para>
<para>Next, the ActionLink#<symbol>isAjaxTarget</symbol> was invoked and because
it returned <varname>true</varname>, ActionLink will be used as the
<literal>Ajax target control.</literal>
</para>
<para>Having found the <literal>Ajax target</literal>, the ActionLink
<symbol>onProcess</symbol> is called.
</para>
<para>Next, the log shows it found the <literal>target AjaxBehavior</literal>
by invoking the AjaxBehavior#<symbol>isRequestTarget</symbol> method, which
returned <varname>true</varname>.
</para>
<para>The AjaxBehavior#<symbol>onAction</symbol> is invoked which returns an
<varname>ActionResult</varname>.
</para>
<para>Finally, the <varname>ActionResult</varname> is rendered to the browser.
</para>
</sect2>
<sect2 id="ajax-trace-log-no-target-control" remap="h3">
<title>Ajax Trace Log - No Ajax Target Control Found</title>
<para>Below we show a log trace where no <literal>Ajax target control</literal>
is found. The most common reason this can occur is if the JavaScript code
that initiates the Ajax request does not send the necessary request parameters
to identify the <literal>Ajax target control</literal>. Another problem is
if no Control is registered ith the <classname>ControlRegistry</classname>,
thus there is no potential <literal>Ajax target control</literal>. This can
occur if no Behavior is added to a Control.
</para>
<literallayout>[Click] [debug] GET http://localhost:8080/mycorp/ajax/ajax-behavior.htm
[Click] [trace] <symbol>is Ajax request</symbol>: <varname>true</varname>
[Click] [trace] request param: <varname>actionLink=link</varname>
[Click] [trace] invoked: AjaxBehaviorPage.&lt;&lt;init&gt;&gt;
[Click] [trace] invoked: AjaxBehaviorPage.onSecurityCheck() : true
[Click] [trace] invoked: AjaxBehaviorPage.onInit()
[Click] [trace] invoked: 'link' ActionLink.onInit()
[Click] [trace] <token>the following controls have been registered as potential Ajax targets:</token>
[Click] [trace] ActionLink: name='link'
[Click] [trace] <symbol>*no*</symbol> target control was found for the Ajax request
[Click] [trace] invoked: 'link' ActionLink.onDestroy()
[Click] [trace] invoked: AjaxBehaviorPage.onDestroy()
[Click] [info ] handleRequest: /ajax/ajax-behavior.htm - 87 ms</literallayout>
<para>Notice from the log that the only request parameters sent is
<varname>actionLink=link</varname>.
</para>
<para>Next, the log prints which controls have been registered as <literal>potential
Ajax targets</literal>. In our example we added an AjaxBehavior to the
ActionLink so the ActionLink is registered as an Ajax target.
</para>
<para>Finally, we see that <symbol>*no*</symbol> Ajax target control was found.
This is because the ActionLink ID attribute, <varname>link-id</varname>,
does not match the incoming request parameter, <varname>actionLink=link</varname>,
hence the ActionLink was not identified as the <literal>Ajax request target</literal>
and no response is rendered.
</para>
</sect2>
<sect2 id="ajax-trace-log-no-target-ajax-behavior" remap="h3">
<title>Ajax Trace Log - No Target AjaxBehavior Found</title>
<para>Below we show a log trace where no <literal>target AjaxBehavior</literal>
is found. This can occur if no <classname>AjaxBehavior's</classname>
<symbol>isRequestTarget</symbol> returns true.
</para>
<literallayout>[Click] [debug] GET http://localhost:9999/mycorp/ajax/ajax-behavior.htm
[Click] [trace] <symbol>is Ajax request</symbol>: <varname>true</varname>
[Click] [trace] request param: <varname>actionLink=link</varname>
[Click] [trace] request param: <varname>link-id=1</varname>
[Click] [trace] invoked: AjaxBehaviorPage.&lt;&lt;init&gt;&gt;
[Click] [trace] invoked: AjaxBehaviorPage.onSecurityCheck() : true
[Click] [trace] invoked: AjaxBehaviorPage.onInit()
[Click] [trace] invoked: 'link' ActionLink.onInit()
[Click] [trace] the following controls have been registered as potential Ajax targets:
[Click] [trace] ActionLink: name='link'
[Click] [trace] invoked: 'link' ActionLink.isAjaxTarget() : true (Ajax target control found)
[Click] [trace] invoked: 'link' ActionLink.onProcess() : true
[Click] [trace] <token>processing AjaxBehaviors for control: 'link' ActionLink</token>
[Click] [trace] invoked: AjaxBehaviorPage.1.<symbol>isRequestTarget()</symbol> : <varname>false</varname>
[Click] [trace] <symbol>*no*</symbol> target AjaxBehavior found for <varname>'link' ActionLink</varname> - invoking AjaxBehavior.isRequestTarget() returned false for all AjaxBehaviors
[Click] [trace] invoked: 'link' ActionLink.onDestroy()
[Click] [trace] invoked: AjaxBehaviorPage.onDestroy()
[Click] [info ] handleRequest: /ajax/ajax-behavior.htm - 80 ms
</literallayout>
<para>We can see from the log that the Ajax request sent the parameters,
<varname>link-id=1</varname> and <varname>actionLink=link</varname> to the server.
</para>
<para>Next we notice that the AjaxBehavior <symbol>isRequestTarget()</symbol>
returned <varname>false</varname>.
</para>
<para>Finally we see that <symbol>*no*</symbol> target AjaxBehavior was found
for the <literal>Ajax target control</literal>, <varname>'link' ActionLink</varname>.
</para>
</sect2>
</sect1>
<sect1 id="ajax-page-action" remap="h2">
<title>Ajax Page Action</title>
<para>Page Actions are <emphasis>page methods</emphasis> that can be invoked
directly from the browser. So instead of handling the Ajax request with a
Control, the request is handled with a <emphasis>page method</emphasis>.
</para>
<para>Similar to the AjaxBehavior <methodname>onAction</methodname> method,
<emphasis>page methods</emphasis> returns an <classname>ActionResult</classname>
containing the data to render to the browser.
</para>
<para>Page Actions have been covered earlier. Please click
<link linkend="page-actions">here</link> for a detailed overview.
</para>
<para>Using a Page Action for handling an Ajax request is no different from
the normal HTTP version. To invoke a Page Action, specify the parameter
<varname>"pageAction"</varname> and the name of the page method eg:
<symbol>"onLinkClicked"</symbol>.
</para>
<para>Here is an example using the
<ulink url="http://www.jquery.com">jQuery</ulink> JavaScript library to make
an Ajax request to a Page Action:
</para>
<programlisting language="javascript">jQuery('#some-link-id').click(function() {
// The ViewCustomerPage url
var url = '$context/view-customers.htm';
// Specify the pageAction parameter and page method to invoke: 'onLinkClicked'
var extraData = 'pageAction=<symbol>onLinkClicked</symbol>';
// Perform the Ajax request
jQuery.get(url, extraData, function(response) {
// Update the target element with the server response
jQuery("#target").html("&lt;p&gt;" + response + "&lt;/p&gt;");
});
}); </programlisting>
<para>The JavaScript snippet above will send a request to the
<classname>ViewCustomerPage</classname> method <symbol>"onLinkClicked"</symbol>,
which returns an <token>ActionResult</token> instance:
</para>
<programlisting language="java">public class ViewCustomerPage extends Page {
...
public <token>ActionResult</token> <symbol>onLinkClicked()</symbol> {
// Formatted date instance that will be returned to the browser
String now = format.currentDate("MMM, yyyy dd HH:mm:ss");
String msg = "PageAction method &lt;tt&gt;onLinkClicked()&lt;/tt&gt; invoked at: " + now;
// Return an action result containing the message
return new <token>ActionResult</token>(msg, ActionResult.HTML);
}
} </programlisting>
<para>The <token>ActionResult</token> contains the data that is rendered to the
client browser. In the example above, the action result is an HTML snippet
containing todays date.
</para>
</sect1>
<sect1 id="ajax-response-types" remap="h2">
<title>Ajax Response Types</title>
<para>The most common server response types are:
</para>
<itemizedlist>
<listitem>
<para>HTML</para>
</listitem>
<listitem>
<para>XML</para>
</listitem>
<listitem>
<para>JSON</para>
</listitem>
</itemizedlist>
<para>Click Controls render themselves as XHTML markup so can be used in
either XML or HTML responses.
</para>
<para>Here is an example showing how to return different types of responses:</para>
<programlisting language="java">public class HelloWorldPage extends Page {
...
public void onInit() {
Behavior htmlBehavior = new DefaultAjaxBehavior() {
public ActionResult onAction() {
String html = "&lt;h1&gt;Hello world&lt;/h1&gt;";
// Return an HTML snippet
return new ActionResult(html, ActionResult.HTML);
}
};
htmlLink.addBehavior(htmlBehavior);
...
Behavior xmlBehavior = new DefaultAjaxBehavior() {
public ActionResult onAction() {
String xml = "&lt;payload&gt;Hello world&lt;/payload&gt;";
// Return an XML snippet
return new ActionResult(xml, ActionResult.XML);
}
};
xmlLink.addBehavior(xmlBehavior);
...
Behavior jsonBehavior = new DefaultAjaxBehavior() {
public ActionResult onAction() {
String json = "{\"value\": \"Hello world\"}";
// Return an JSON snippet
return new ActionResult(json, ActionResult.JSON);
}
};
jsonLink.addBehavior(jsonBehavior);
}
} </programlisting>
</sect1>
<sect1 id="ajax-error-handling" remap="h2">
<title>Ajax Error Handling</title>
<para>If an exception occurs while processing an Ajax request and the application
is in <literal>development</literal> mode, the exception
<literal>stackTrace</literal> is returned to the browser.
</para>
<para>If an exception occurs while processing an Ajax request and the application
is in <literal>production</literal> mode, a simple error message is returned.
</para>
</sect1>
</chapter>