This entry is part 7 of 8 in the series Dojo Quick Start Guide

Note: The Dojo Quick Start Guide posts – while still relevant – are a bit dated. Please visit http://dojotoolkit.org/documentation/ for expert tutorials and API documentation to help you get up and running with Dojo.

Ajax is an acronym for “Asynchronous JavaScript and XML”, a technology employed to send and receive data on the fly. It can be used to update sections of a website from any number of remote sources, send data to the server and pass responses back and forth, all without ever refreshing the web page.

Having been versed on some essential Dojo methods already, now we’ll move on the the bread and butter of Ajax: the XMLHttpRequest (or XHR for short).

Dojo has several XHR methods available using common HTTP verbs: POST, GET, PUT, and DELETE. To prepare, we need to create a file with some text to load in when making our HTTP request.

Create a file named sample.txt in your js/ folder with sample text:

I am a remote file.
We used Ajax to put this text
in our page.

And modify the skeleton.html to have some basic markup and style:


I am some Inner Content. I am going to be replaced
The XHR methods use dojo.Deferred behind the scenes to handle callbacks. This is beyond the scope of a QuickStart, but extremely useful in practice. If you would like to learn more about callbacks and the various way to set them up, visit the Robust Promises with Dojo Deferred blog post or the dojo.Deferred API pages.

Getting data

The first stepping stone is dojo.xhrGet, which will return the contents of a GET call on a URL. The XHR methods share a lot of common parameters. Most important are the url: (our destination) and handleAs: (how we handle what is coming back). When the data arrives, it will be passed the the load: function we define:

var init = function(){
	var contentNode = dojo.byId("content");
	dojo.xhrGet({
	    url: "js/sample.txt",
	    handleAs: "text",
	    load: function(data,args){
		// fade out the node we're modifying
		dojo.fadeOut({
		    node: contentNode,
		    onEnd: function(){
			// set the data, fade it back in
			contentNode.innerHTML = data; 
			dojo.fadeIn({ node: contentNode }).play();    
		    }
		}).play();
	    },
	    // if any error occurs, it goes here:
	    error: function(error,args){
		console.warn("error!",error);
	    }
	});
}; 
dojo.ready(init);

You will notice we’ve combined techniques above. The content will fade out, be replaced by the received data, and fade back in using methods we’ve learned to this point. It was almost too easy.

A single handle argument can be used instead of load and error, handling both success and failure cases in a common function:

var init = function(){
     dojo.xhrGet({
         url: "js/sample.txt",
         handleAs: "text",
         handle: function(data,args){
     	if(typeof data == "error"){
     	    console.warn("error!");
     	    console.log(args);
     	}else{
     	    // the fade can be plugged in here, too
     	    dojo.byId("content").innerHTML = data;
     	}
         }
     });
};
dojo.ready(init);

XHR has limitations. The big one being that url is not cross-domain. You cannot submit the request outside of the current host (e.g. to url:"http://google.com"). It is a known limitation and a common mistake when getting excited about Ajax. Dojo provides alternatives like dojo.io.iframe and dojo.io.script for more advanced usage.

You also may experience problems with the Ajax samples if you are using Dojo from the local file system, rather than through a web server (use localhost rather than file:///). Browsers place stricter security policies with XHR on the local file system than from web servers. While most of these examples do work from the file system, it is recommended you have a web server accessible to host the Dojo source, and your tests.

A full list of XHR parameters is available at the Dojo XHR API page. We are only going to skim the surface here.

Sending Data

All Dojo XHR methods are bi-directional. The only difference is the method. Using dojo.xhrPost, we use the POST method, embedding the data in the request (as opposed to the query string as with dojo.xhrGet). The data can be set directly as an object passed to the content parameter:

dojo.ready(function(){
     dojo.xhrPost({
         url:"submit.html",
         content: {
     	   "key":"value",
     	   "foo":42,
     	   "bar": {
     	     "baz" :"value"    
     	   }
         },
         load: function(data,ioargs){
     	   console.log(data);
         }
     });
});

Or more commonly, conveniently copied and converted from a form element! First, make a simple unobtrusive form in the skeleton.html:

Then, add in some JavaScript to submit the form by using dojo.connect to listen to the onSubmit event, and post the contents of the form to an alternate URL:

// submit the form 
var formSubmit = function(e){
	// prevent the form from actually submitting
	e.preventDefault(); 
	// submit the form in the background	
	dojo.xhrPost({
	    url: "alternate-submit.php",
	    form: "mainForm",
	    handleAs: "text",
	    handle: function(data,args){
	        if(typeof data == "error"){
	            console.warn("error!",args);
	        }else{
	            // show our response 
	            console.log(data);
	        }
	    }
	});
};
dojo.ready(function(){
	var theForm = dojo.byId("mainForm");
	// another dojo.connect syntax: call a function directly	
	dojo.connect(theForm,"onsubmit","formSubmit");	
});
Notice e.preventDefault() being used again. The default nature of a form being submitted to to visit a new page, and we want to prevent that from happening.

An example alternate-submit.php would look like:

"; 
	foreach($_REQUEST as $key => $var){
		print "
  • ".$key." = ".$var."
  • "; } print ""; ?>

    Object Data

    Getting text back from the server is nice, but the really great stuff comes when you start passing JavaScript objects around. Using a different handleAs: attribute, we can alter how Dojo handles the response data. Make a new file named simple-object.json to load:

    {
    	foo: "bar",
    	name: "SitePen",
    	aFunction: function(){
    		alert("internal function run");	    
    	},
    	nested: {
    	    sub: "element",
    	    another: "subelement"
    	}
    }

    We’ll target our xhrPost url: at the new file, and use handleAs: "json". This automatically converts the response data into a JSON object we can conveniently use:

    var postData = function(){
    	dojo.xhrPost({
    	    url: "js/simple-object.json",
    	    handleAs: "json",
    	    load: function(data,ioargs){
    	        // success: set heading, run function
    	        dojo.byId("testHeading").innerHTML += " by: "+data.name;
    	        if(data.aFunction && data.aFunction()){
    	            // we just ran data.aFunction(). should alert() ... 
    	        } 
    	    }
    	});
    };
    dojo.ready(postData);
    dojo.xhr exists as a wrapper for each of the method specific calls. If you use dojo.xhr, add another property, method, with the value in uppercase, e.g. GET, PUT, POST, or DELETE.

    This allows us to send literally any kind of data back and forth across the wire, without ever interrupting the user experience.