Server communication

Various ways of exchanging data with a server

Although HTML5 app can be distributed for offline usage, and indeed the simple game that we developed in the previous chapter doesn't need to be played with an internet connection if all the resources are local, it is really easy to add online features into the mix. Let's start a new App to see how it is done.

The most common way of exchanging data between client and server is through Javascript Object Notation (JSON) files. So let's create a data folder, and a file called someData.json in it.

Now edit this file to make it look like this:

    {
        "testString" : "This is just a test to see how we can retrieve data from a server",
        "testNumber" : 42
    }

Now let's edit our main app file (test.js):

    App = function()
    {
        this.load = function()
        {
            // load some data from the server
            this.myData = {};
            wade.loadJson('data/someData.json', this.myData, 0, 1);
        }
    
        this.init = function()
        {
            // create a text object
            var textSprite = new TextSprite(this.myData.data.testString, '22px Arial', 'blue', 'center');
            textSprite.setMaxWidth(400);
            var textObject = new SceneObject(textSprite);
            wade.addSceneObject(textObject);
        }
    };

This is the easiest way of loading a JSON file synchronously, in the same way we have been loading other resources for our apps. There is only a minor difference: at line 6 we are creating an empty object, and in the loadJson call at line 7 we are telling WADE to use that object to store the JSON data. Also note the other arguments: a callback function to execute (we are passing 0 to indicate that we don't need it), and whether to force reloading the data (we are passing 1 because we want to do this), rather than getting a cached version if there is one.

So after the loadJson call completes, our myData object will have a data field that contains all the JSON data that has been loaded. We are then using this data (specifically the testString field in this data) at line 13 to display some text on the screen. If you save and run your app, it will look like this:

 

We can do the same things asynchronously, which is more useful in many situations. It's the same principle, but we call preloadJson rather than loadJson, and we define a callback function to execute when the JSON file is finished loading. Our app will then look like this (no load function this time):

    App = function()
    {
        this.init = function()
        {
            // create a text object
            this.myData = {};
            var textSprite = new TextSprite('Click to get data', '22px Arial', 'blue', 'center');
            textSprite.setMaxWidth(400);
            this.textObject = new SceneObject(textSprite);
            wade.addSceneObject(this.textObject);
        }
    
        this.onMouseDown = function()
        {
            this.textObject.getSprite(0).setText('Retrieving data...');
            wade.preloadJson('data/someData.json', this.myData, this.updateText_(), 1);
        }
    
        this.updateText_ = function()
        {
            var that = this;
            return function()
            {
                that.textObject.getSprite(0).setText(that.myData.data.testString);
            }
        }
    };

We are now displaying a 'Click to get data' string to start with, then when the user clicks, we are changing that string to 'Retrieving data...', and we are getting the data asynchronously with a preloadJson call. In this call, we are telling WADE to use the myData object to store the data, and we are passing it a function to execute when it's done.

Incidentally, note that we are using a closure to pass the callback function. If you don't know what that is, do read an explanation here, as it is a very powerful feature of javascript that you will end up using very often. In WADE, there is a consistent naming convention for these constructs: if a function is intended to just return another function, its name always ends with an underscore. Although you are obviously free to implement your own naming conventions, remember that in a weakly-typed language such as javascript it's really quite important to have one, and consistently stick to it.

Anyway, if you run the above code, since your data is in local files, you'll barely see the 'Retrieving data...' text, as the loading (although asynchronous) is going to be almost instantaneous. If you were getting the data from a server, it would probably take a bit longer (a few milliseconds) for the request to complete.

Although we are doing this with some static data, you would do it exactly the same way for dynamic data. For example, the path to your JSON file could be 'http://www.example.com/doSomething.php'. As long as the server script that is executed returns JSON data, that is perfectly valid - and the server could return different data every time a request occurs. Obviously the server can be coded in any programming language (notably it can be coded in javascript itself if you use node.js), and most if not all programming languages support JSON - many natively, others through free libraries.

So it's very easy to retrieve data from a server before the app starts (with loadJson) or while the app is running (with preloadJson). But how do you send data to a server?

There are at least two ways. If you only want to send a little amount of data, it can be very convenient to just include your data in the URL itself, for example:

    this.serverResponse = {};
    var url = 'http://www.example.com/doSomething.php?this_is_the_data_i_am_sending';
    wade.preloadJson(url, this.serverResponse, 0, 1);

Or even better:

    this.serverResponse = {};
    var dataToSend = "This is the data I am sending to the server. It's not much.";
    var url = 'http://www.example.com/doSomething.php?' + encodeURIComponent(dataToSend);
    wade.preloadJson(url, this.serverResponse, 0, 1);

This is fine when you don't have a lot of data to send, but there is a limit to how much data you can send this way. There is no theoretical limit, but some browsers just don't like URL's that are too long, so it's better to never exceed 2048 characters for a URL.

For when you want to send more data, or simply more structured data, WADE provides a very useful postObject function, that allows you to send a whole javascript object to the server (the object will be internally serialized to JSON before being transferred).

    wade.postObject(url, object, this.onServerResponse_());

The server will then receive a POST request, with a 'data' parameter that is a JSON string representing the object that you sent.

Your onServerResponse function will then look like this:

    this.onServerResponse_ = function()
    {
        var that = this;
        return function(xhr, status)
        {
            if (status == 'error' || !xhr.responseText)
            {
                // an error occurred
            }
            else
            {
                // no errors - do something with xhr.responseText
            }
        };
    };

Note that postObject accepts an additional optional argument (not shown above), which is a set of parameters to attach to the POST request. In most cases you won't care, but it may be useful to enhance your web app's security, for example if you want to attach a token to prevent cross-site-request-forgery.