In Chapter 3, we added an Ajax request to the News application, transforming the application from a static data reader to a dynamic application serving up new stories from multiple feeds in the background. There is a huge difference in user experience when your application can let the user know that something new exists and present it to her immediately.
You aren’t required to use the Prototype functions. You can use the XMLHttpRequest object directly, and you will be required to if your data protocols are SOAP-based or if they are anything other than simple XML, JSON, or text-based web services. There are many references for XMLHttpRequest if you’d like to explore this more directly. Any fundamental JavaScript reference will give you an overview, but for more in-depth information, look for Ajax-specific references such as Anthony Holdener’s Ajax: The Definitive Guide (O’Reilly). For a more basic introduction, you can review the tutorial at https://developer.mozilla.org/en/XMLHttpRequest, which, while focused on Firefox, is nonetheless a good introduction to XMLHttpRequest.
The Ajax class functions, which are a basic feature of the Prototype JavaScript library, are included with webOS because they encapsulate the lifecycle of an XMLHttpRequest object and handlers into a few simple functions. The next few pages will explore these functions to show you how Prototype can help you integrate dynamic data into your application.
Palm webOS applications are run from file:// URLs and thus aren’t restricted by the single-origin policy that makes mixing services from different websites difficult.
Back in Chapter 3, we added an Ajax.Request
object to News to sync the web
feeds to the device, but didn’t describe the object or the return
handling in any detail. Ajax.Request
manages the complete Ajax lifecycle, with support for callbacks at
various points to allow you to insert processing within the lifecycle
where you need to. In Chapter 3, we used updateFeedRequest
to initiate the Ajax
request:
// updateFeedRequest - function called to setup and make a feed request updateFeedRequest: function(currentFeed) { Mojo.Log.info("URL Request: ", currentFeed.url); var request = new Ajax.Request(currentFeed.url, { method: "get", evalJSON: "false", onSuccess: this.updateFeedSuccess.bind(this), onFailure: this.updateFeedFailure.bind(this) }); },
An Ajax.Request
is created
using the new
operator, the only way
that a request can be generated, and initiates an XMLHttpRequest as soon
as it is created. In this case, a get
request is initiated on the URL defined in currentFeed.url
, and two callbacks are defined
for success or failure of the request. You can also make post
requests and define other callbacks,
which map to the request lifecycle shown in Table 6-3.
Table 6-3. Ajax.Request callbacks by lifecycle stage
Callback | Lifecycle stage |
---|---|
| Created |
| Created |
| Initialized |
| Request Sent |
| Response being received (per packet) |
| Response Received |
| Response Received |
To clarify:
onCreate
Is available only to responders and is not available as a property of Ajax.Request
.
on###
When specified (where ###
is an HTTP status code, such as on 403), it is invoked in place of
onSuccess
in the case of a
success status, or onFailure
in
the case of a failure status.
onComplete
Is called only after the previous potential callback—either onSuccess
or onFailure
(if specified)—is
called.
Ajax requests are asynchronous by default, one of the virtues of
Ajax. While Ajax.Request
supports an
override, the synchronous XMLHttpRequest
has been
disabled in webOS. Since the UI and applications run as part of a common
process, this is necessary to preserve UI responsiveness. It may be
supported in a later release when there is concurrency support.
There are more Ajax.Request
options available. Ajax.Request
is
covered thoroughly in most Prototype references, including the Prototype
1.6 API reference available at http://www.prototypejs.org/api.
An Ajax.Response
object is
passed as the first argument to any callback. In our sample, we have used the HTTP status codes under
the status property and the responseText
and responseXML
properties. Table 6-4 provides a summary of the full
list of properties.
Table 6-4. Ajax.Response properties
Property | Type | Description |
---|---|---|
| Integer | Current lifecycle state: 0 = Uninitialized 1 = Initialized 2 = Request Sent 3 = Interactive 4 = Complete |
| Integer | HTTP status code for the request |
| String | HTTP status text corresponding to the code |
| String | Text body of the response |
| XML or Document | If content type is application/xml, then XML body of the response; null otherwise |
| Object | If content type is application/json, then JSON body of the response; null otherwise |
| Object | Sometimes JSON is returned in the X-JSON header instead of response text; if not, this property is null |
| Object | Original request object |
| Object | Actual XMLHttpRequest object |
We haven’t discussed JSON specifically, but it is an increasingly
important tool for developers. Prototype includes some powerful JSON
tools in Ajax.Request
, which supports automatic
conversion of JSON data and headers. If you’re handling structured data,
you should look at JSON, particularly for data interchange.
Prototype includes some additional functions for consolidating listeners for Ajax callbacks, called responders, and for updating DOM elements directly from an Ajax request.
If you’re doing multiple Ajax requests, you might find it useful to set up responders for common callbacks rather than setting callbacks with each request. This is particularly applicable to error handlers or activity indicators. The following example shows the use of setup responders to manage a spinner during feed updates and to handle Ajax request failures:
Ajax.Responders.register({ onCreate: function() { spinnerModel.value = true; this.controller.modelChanged(spinnerModel, this); }.bind(this), onSuccess: function(response) { spinnerModel.value = false; this.controller.modelChanged(spinnerModel, this); this.process.Update(response); }.bind(this), onFailure: function(response) { this.spinnerModel.value = false; this.controller.modelChanged(spinnerModel, this); var status = response.status; Mojo.Log.info("Invalid URL - Status returned: ", status); Mojo.Controller.errorDialog("Invalid feed - http failure: "+status); }.bind(this) });
In this sample code, each responder is defined using the
callback property and a function literal. The first, onCreate
, is
available only to responders and not in an Ajax.Request
object. It starts the spinner,
while the other two, onSuccess
and
onFailure
,
stop it while performing some appropriate postprocessing.
Responders can be unregistered, but you would need to avoid using function literals when you register them, as the previous example did. The responders would need to be defined first, then registered and unregistered with a reference to that definition.
Ajax.Updater
and Ajax.PeriodicalUpdater
each make an Ajax request and update the contents of a
DOM container or element with the response text. Ajax.Updater
will perform an update request
once, while Ajax.PeriodicalUpdater
will perform the requests repeatedly, with a delay option to extend
the interval between updates when responses are unchanged. Note that
Ajax.PeriodicalUpdater
uses
JavaScript timers and won’t wake the device.
18.226.88.151