Sunday 21 August 2011

Dojo

Ajax has been a buzzword around for some time now (as far as you could call some time a lot of time ;) ) and is one of the concepts which have changed the development for the web quite drastically.
Dojo provides a solid set of battle-tested XHR wrapper functions to allow you to build Ajax interactions with confidence, use a unified API, and handle forms with ease. These APIs are built into Dojo Base, so you can use them in any page that includes dojo.js. Read on to learn how easy it is to build powerful Ajax interactions with Dojo.
The XMLHTTP request object (XHR for short) is one of the basic building blocks for constructing responsive Ajax-drive interactions. By allowing you to retrieve data on the user’s behalf without refreshing the whole page the XHR object provides tremendous, but cross-browser XHR usage is beset by memory leaks, divergent APIs, a lack of built-in form encoding from JavaScript, and painful corner cases when de-serializing response data.


All XHR functions follow the same pattern in the property-bag configuration options, passed to whichever function is called. They include:
  • url - the endpoint to connect to and load data from. This must be on the same host and port as the serving page, a security limitation of XHR.
  • handleAs - describes which formatter to use on the incoming data. Defaults to ‘text’, so any response data comes back as a plain string. Available options out of the box are: “json” (to convert the data to a JSON object), “javascript” (to load and execute JS fragments), “json-comment-optional” (to deprecate warnings about the poor security of client-side JSON parsing) and xml.
  • timeout - a time in MS to wait before giving up the XHR call, and throwing an error to the error callback.
  • sync - a boolean to determine if the XHR call should be synchronous or asynchronous. Setting sync:true will cause the browser to stop the chain of execution until the data is returned. Defaults to false.
  • form - a DOM Node of a <form> element, or a string ID of a <form> element, used to collect data to be sent along with the request. The form is passed through dojo.formToObject and is mixed into the content: attribute of the XHR call
  • content - an object to be sent along with
Example usage:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// post some data, ignore the response:
dojo.xhrPost({
form: "someFormId", // read the url: from the action="" of the <form>
timeout: 3000, // give up after 3 seconds
content: { part:"one", another:"part" } // creates ?part=one&another=part with GET, Sent as POST data when using xhrPost
});

// get some data, convert to JSON
dojo.xhrGet({
url:"data.json",
handleAs:"json",
load: function(data){
for(var i in data){
console.log("key", i, "value", data[i]);
}
}
});
Introduced was the load: function, which is explained in the XHR Callbacks section below.

There are three methods one can attach to the XHR Options object to determine what to do when the data comes back.
  • load - executed when a successful Ajax call is complete. Is passed the data and an object of the XHR properties.
  • error - executed when an Ajax call times out, or otherwise fails. Is passed the error and an object of the XHR properties.
  • handle - combination of load and error callbacks, fired when either of the two conditions are met. In the success case, behaves just like load:, and in the failure case like error:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
dojo.xhrPost({
form:"someForm",
load: function(data, ioArgs){
// ioArgs is loaded with XHR information, but not useful in simple cases
// data is the response from the form's action="" url
},
error: function(err, ioArgs){
// again, ioArgs is useful, but not in simple cases
console.error(err); // display the error
}
});
// or like this:
dojo.xhrPost({
form:"someForm",
handle: function(dataOrError, ioArgs){
if(dojo.isString(dataOrError)){
// handleAs defaults to text, so look for a string here
}else{
// this must be an error object
}
}
});
Alternately, you can "use plain Deferred's" to register callbacks. They are slightly more difficult to work with, but the concept is the same.

Customizing an Ajax Request

Web developers need flexibility in Ajax requests to accomplish different tasks. Reasons for using Ajax calls include, but are not limited to:
  • Loading static data from the server
  • Accessing XML or JSON data from a web service
  • Sending form data to the server
  • Refreshing content on a page
Obviously, one type of request cannot accommodate all Ajax goals. Through customization of the request, dojo.xhrGet and dojo.xhrPost can handle each of the situations presented above. Customization of the request takes place in each method's single argument: an object containing request properties and desired values. Let's review the most-used request options available:
  • url - The URL to make the request to.
  • handleAs - A string representing the form of data we expect returned. Possible formats include: "text" (the default), "json", "javascript" (fragments to load and execute), and "xml".
  • timeout - Time in milliseconds before considering the request a failure. The error handler is triggered.
  • content - A key-value object containing data to provide to the server. Depending on use of xhrGet or xhrPost, this data will either be translated to the query string or set as the post body.
  • form - A utility option which populates the content option from keys and values in a form. If you don't specify a URL, and are using this option, it will try to use the URL as specified in the form's "action" property. Also, if you specify any content, it will override anything in the form, so typically you'll use either content or form, but not both.
The options above manage how the request is sent, but what about the response? The answer to that lies in three handler functions, often referred to as callback functions or just callbacks, which are also provided to the request object:
  • load(response, ioArgs) - The callback that fires when the request successfully completes. The first argument of load is the result of the request in the format designated by the handleAs option.
  • error(errorMessage) - The callback that fires when the request fails. The first argument is the error message, if available.
  • handle(response, ioArgs) - The callback that fires regardless of request success or failure.
Callbacks are important in handling data from the returned from the request and knowing their success or failure. The load or error method is called first, depending on result, and the handle callback fires next.

Examples: dojo.xhrGet and dojo.xhrPost

The following are some very common uses of dojo.xhrGet and dojo.xhrPost.

Refresh a Node's Content

1
2
3
4
5
6
7
8
9
10
11
12
13
// Using dojo.xhrGet, as very little information is being sent
dojo.xhrGet({
    // The URL of the request
    url: "get-content.php",
    // The success callback with result from server
    load: function(newContent) {
        dojo.byId("contentNode").innerHTML = newContent;
    },
    // The error handler
    error: function() {
        // Do nothing -- keep old content there
    }
});

Features

Asynchronous communication

One important feature of Ajax applications is asynchronous communication of the browser with the server: information is exchanged and the page's presentation is updated without a need for reloading the whole page. Traditionally, this is done with the JavaScript object XMLHttpRequest. Dojo provides an abstracted wrapper (dojo.xhr) around various web browsers' implementations of XMLHttpRequest, and dojo.io also supports other transports (such as hidden IFrames) and a variety of data formats. Using this approach, it is easy to have the data a user enters into a form sent to the server "behind the scenes"; the server can then reply with some JavaScript code that updates the presentation of the page.

Packaging system

Dojo provides a packaging system to facilitate modular development of functionality in individual packages and sub-packages; the base Dojo "bootstrap" script initializes a set of hierarchical package namespaces -- "io", "event", etc. -- under a root "dojo" namespace. After initialization of the root namespace any Dojo package can be loaded (via XMLHttpRequest or other similar transport) by using utility functions supplied in the bootstrap. It is also possible to initialize additional namespaces within or parallel to the "dojo" namespace, allowing extensions of Dojo or the development of private Dojo-managed namespaces for third-party libraries and applications.
Dojo packages can consist of multiple files, and can specify which files constitute the entire package. Any package or file can also specify a dependency on other packages or files; when the package is loaded, any dependencies it specifies will also be loaded.
Workarounds for cross-domain loading of most Dojo packages are provided (though this requires a specialized build of Dojo).
Dojo also provides a mechanism for building "profiles"; the build system takes as input a list of packages, and uses Rhino to create a single compressed JavaScript file containing those packages and all their dependencies. This allows all necessary code to be loaded and initialized at once, and permits caching of the code (most web browsers do not cache files loaded via XMLHttpRequest). Pre-built profiles for some common use cases are available for download from the same location as the full toolkit.

Client-side data storage

In addition to providing support functions for reading and writing cookies, Dojo also provides a local, client-side storage abstraction named Dojo Storage. Dojo Storage allows web applications to store data on the client-side, persistently and securely and with a user's permission. It works across existing web browsers, including Internet Explorer, Firefox, and Safari. When included in a web page, Dojo Storage determines the best method for persistently storing information. On Firefox 2, it uses native browser persistence; on other browsers it uses a hidden Flash applet. With Flash 6+ being installed on about 95% of computers connected to the web this makes the storage mechanism accessible for much of the web's installed base. For a web application that is being loaded from the file system (i.e. from a file:// URL), Dojo Storage will transparently use XPCOM on Firefox and ActiveX on Internet Explorer to persist information. The programmer using Dojo Storage is abstracted from the storage mechanism used and is presented with a simple hash table abstraction, with methods such as put() and get(). Dojo Storage is not supported in versions later than the 1.3 release.

Server-side data storage

As of January 2007, Dojo includes the following example server-side datastore implementations in the dojo.data namespace
  • CsvStore: a read-only store that reads tabular data from comma-separated values files
  • OpmlStore: a read-only store that reads hierarchical data from OPML format files
  • YahooStore: a read-only store that fetches search results from the Yahoo! Search web service
  • DeliciousStore: a read-only store that fetches bookmarks from the del.icio.us web service
  • RdfStore: a read-write store that uses SPARQL to talk to RDF data servers including, for example, the Rhizome RDF application server.

Support for Adobe Integrated Runtime (AIR)

Dojo can be used in JavaScript-based Adobe AIR applications. It has been modified to meet AIR's security requirements.
Sitepen, a Dojo consulting company, has made an Adobe AIR application called "Dojo Toolbox" using Dojo. It includes an API viewer, and a GUI to Dojo's build system. Normally, the build system is run from within Rhino, but in this AIR application the build system can be run from AIR, without use of java.

No comments:

Post a Comment