Passing Data with Dojo

Introduction

Dojo is a toolkit devoted to making web applications easier to write. One feature that's been recieving a lot of attention lately is fetching data in the background using Javascript. GMail uses it to make the pages seem faster by fetching them before they're needed. Google Maps grabs parts of the map as you scroll around, so you don't have to load the whole thing.

As with all good things in Javascript, there's a lot of cross-browser compatiblity and user friendliness issues to deal with. The Dojo Toolkit can help with that. This article will just introduce the basic ideas and techniques for passing data to and from a server.

Introducing dojo.io.bind()

The Dojo method for getting a file is dojo.io.bind(). You need to pass it some arguments in the form of an object with some properties. For example, you could call it like this:

var params = new Object();
params.url = "something.txt";
params.load = someFunction;
dojo.io.bind( params );
However, there is a shorter way. Here's a complete example that fetches a text file from a server.

Text fetched from the server:
Hello world!
I'm just a plain text file!
Client-side Javascript:
function dumpData( type, data, evt ) {
    var msg = "Data dump:\n" + data;
    alert( msg );
}

dojo.io.bind({
    url: demoLocation + "demo_text.txt",
    load: dumpData,
    mimetype: "text/plain"
});
Try it.

The curly braces are part of a shorthand syntax for creating an object in Javascript. It's sort of like declaring an array:

var arr = [ 1, 2, 3 ]
var obj = {
    prop1: 1,
    prop2: 2,
    prop3: 3
};
arr[ 0 ] + obj.prop1 // 1 + 1 = 2

Note that in the above example, we pass three parameters to dojo.io.bind():

Handling Errors

Not everything goes well. It's good practice to define a function for handling errors, in case something goes wrong. You can tell dojo.io.bind() what to call by specifying an error parameter.

Client-side Javascript:
function errorHandler( type, error ) {
    var msg = "Something went horribly wrong ...\n" +
        error.message;
    alert( msg );
}

dojo.io.bind({
    url: demoLocation + "this_does_not_exist",
    load: dumpData,
    error: errorHandler,
    mimetype: "text/plain"
});
Try it.
Note: The message in the object isn't designed for users. You should write your own general error message and ask the user to report the technical message.

In case you're wondering, the "SampleTransport" part tells you how Dojo is communicating with the server.

Running Javascript

So far, we've just loaded some text. You can also load and execute some Javascript from the server. It's run automatically, so the load handler is unimportant.

Javascript fetched from the server:
function helloWorld() {
    alert( "Hello world!\n" +
        "I'm some javascript." );
    return "I have returned";
}

helloWorld();
Client-side Javascript:
function showReturnValue( type, evaldObj ) {
    alert( "The script returned\n" +
           evaldObj );
}

dojo.io.bind({
    url: demoLocation + "demo_javascript.js",
    load: showReturnValue,
    error: errorHandler,
    mimetype: "text/javascript"
});
Try it.
Note: The value recieved by your handler is the value of the last line of Javascript

Using a Query String

So far you've gotten some text and some code. You can read it and run it. How about sending some data to the server? The most basic way to do this is to add a query string to the URL. It's just an extra part in the form of ?var1=something&var2=something_else

Server-side PHP:
<?php

print "I'm a PHP script. This is what I recieved:\n";
// $_GET is a PHP array that holds recieved data
// print_r prints it out nicely
print_r( $_GET );

?>
Client-side Javascript:
dojo.io.bind({
    url: "demo_php.php?foo=bartok",
    load: dumpData,
    error: errorHandler,
    mimetype: "text/plain"
});
Try it.

Submitting a Form

Another classic way to send data is using a form. The major advantage of using dojo.io.bind() instead of a submit button is that the page doesn't change. This means you can get data from the server while the user is still editing the form (a la Google Suggest). You can do it just by passing the form node as a parameter, aptly named formNode.

Client-side HTML:
<form id="demoForm" action="demo_php.php">
<input type="text" name="baz" value="something" />
<input type="submit" name="submit" value="Don't press!" />
</form>

Client-side Javascript:
dojo.io.bind({
    // look ma, no URL!
    // (the form's action property is used)
    load: dumpData,
    error: errorHandler,
    formNode: document.getElementById( "demoForm" ),
    mimetype: "text/plain"
});
Try it.
Note: The URL in the form's action property is where the form gets submitted. Passing a URL to bind() is useless. Also, GET is always used.

Submitting Forms Made on the Fly

Sometimes you just want to send some data back to the server, without the user fiddling with a form. Query strings are cryptic and a pain to write by hand. If you use a form on the page, then you'd have to disable it. Instead, you can just create the form using Javascript and pass it directly to dojo.io.bind(). It never appears on the page.

Client-side Javascript:
// helper code omitted ...
function buildForm( loc, data ) {
    var form = document.createElement( "form" );
    form.action = loc;
    for( prop in data ) {
        buildFormHelper( form, prop, data[prop] );
    }
    return form;
}
(show helper code)
Client-side Javascript:
function makeInput( form, name, value ) {
    var input = document.createElement( "input" );
    input.name = name;
    input.value = value;
    form.appendChild( input );
    return true;
}

function buildFormHelper( form, prev, data ) {
    var name;
    if( ( typeof data == "object" ) ||
        ( typeof data == "array" ) ) {
        for( prop in data ) {
            name = prev + "[" + prop + "]";
            // recurse
            buildFormHelper( form,
                             name,
                             data[ prop ] );
        }
    } else {
        // literal
        name = prev;
        makeInput( form, name, data );
    }
}

function buildForm( loc, data ) {
    var form = document.createElement( "form" );
    form.action = loc;
    for( prop in data ) {
        buildFormHelper( form, prop, data[prop] );
    }
    return form;
}
(hide helper code)
(More) client-side Javascript:
var data = new Object();
data.foo = "bar";
data.arr = [ 1, "hello", 3 ];
dojo.io.bind({
    load: dumpData,
    error: errorHandler,
    formNode: buildForm( "demo_php.php", data ),
    mimetype: "text/plain"
});
Try it.

Limitations

There are some limitations placed on your script.

Conclusion

You can start making your applications more interactive right now, and Dojo will make it easier. Once you have some experience with the basics, you can also use Dojo to make your application friendlier. There's much more planned for the toolkit, so check DojoToolkit.org for more information and releases.