Using Ajax Requests in UWA

The UWA JavaScript framework provides to developers various APIs for communicating with web-services using cross-domain Ajax requests.

These APIs are easy to use and specialized for different data types.

General thoughts

Ajax requests are asynchronous by design: calling a method doesn't imply getting an immediate return value. This return value might take some time, depending on the server, the traffic, the bandwidth, etc.

Therefore, you have to pass a callback function to the Ajax request method. This callback function will process the data sent back by the web service, as soon as it arrives.

A generic use of Ajax with UWA would make use of the advanced UWA.Data.request method:

var MyWidget = {

    dataInit: function () {

        UWA.Data.request('http://api.example.org/request', {
            method: 'GET',
            type: 'xml',
            cache: 3600,
            onComplete: MyWidget.dataProcessor,
            onFailure: MyWidget.dataError
        });

    },

    dataProcessor: function(data) {
        // do your data processing here
    },

    dataError: function(error) {
        // do your data error here
    }
};

// widget.onLoad() is triggered when the app is fully loaded
// or when the preferences are validated
widget.addEvent('onLoad', MyWidget.dataInit);

When returned by the web service, the data will be passed to the callback function as the first argument. In the above example, the Ajax response is passed as the first argument to MyWidget.dataProcessor.

If the request fails (404, timeout, etc...) or if a JavaScript error happens in the dataProcessor function, the onFailure callback will be called with an Error object as the first argument.

It is important to implement the onFailure callback to provide a good user experience even if your service is not available.

The type of the response depends on the Ajax method used, or, if the developer uses the UWA.Data.request method, as seen above, the type will depend on the "type" argument of the request. In the above example, we used the "xml" type.

In addition to UWA.Data.request, UWA.Data offers four easier, more specialized methods, optimized for various data types.

getText request

This is the simplest Ajax method, to be used when the web-service simply returns a text message, which may contains HTML code or specially formatted data, but will nevertheless be sent as a string. When using UWA.Data.getText, it's up to the developer to code methods to process this string correctly.

Here is a way of properly retrieving a string using the UWA.Data.getText method:

var MyWidget = {

    dataInit: function () {
        UWA.Data.getText(
            'http://api.example.org/request',
            MyWidget.dataProcessor
        );
    },

    dataProcessor: function(text) {
        widget.setBody({
            tag: 'p',
            text: 'Hello ' + text + '!'
        });
    }
};

// widget.onLoad() is fired when the app is fully loaded
// or when the preferences are validated
widget.addEvent('onLoad', MyWidget.dataInit);

getJson request

JSON is JavaScript's native data description format. If the web-service returns data using this format, the UWA.Data.getJson method allows you to handle it easily, even if it's complex data.

Here is a simple JSON code that a web-service might returns:

{
  "message": "Hello UWA World!",
  "from": "Example.org",
  "to": "UWA user"
}

This is how it could be handled using the UWA.Data.getJson method:

var MyWidget = {

    dataInit: function () {
        UWA.Data.getJson(
            'http://api.example.org/request',
            MyWidget.dataProcessor
        );
    },

    dataProcessor: function(json) {

        widget.setBody([
            {
                tag: 'h2',
                'class': 'messageFrom',
                text: 'Message from ' + json.from
            },
            {
                tag: 'div',
                'class': 'messageTo',
                text: 'To:  ' + json.to
            },
            {
                tag: 'p',
                'class': 'messageText',
                text: json.message
            }
        ]);
    }
};

// widget.onLoad() is fired when the app is fully loaded
// or when the preferences are validated
widget.addEvent('onLoad', MyWidget.dataInit);

More info about JSON format can be found here: http://json.org

getXml request

XML is a very common standard data description language, used by most web-services to get and send data. It is the basis of most data exchange on the Web today.

The easiest way to process XML using JavaScript is through the browser DOM methods. UWA lets developers retrieve XML as a document object using the UWA.Data.getXml method.

Here is a simple XML code that a web-service might send back:

<example>
    <message>Hello UWA World!</message>
    <from>Example.org</from>
    <to prefix="Mr.">UWA user</to>
</example>

This is how it could be handled using UWA.Data.getXml method:

var MyWidget = {

    dataInit: function () {
        UWA.Data.getXml(
            'http://api.example.org/request',
            MyWidget.dataProcessor
        );
    },

    dataProcessor: function(xml) {

        //
        // Storing data extracted from XML as an object is a best practice
        // for manipulating XML using JavaScript. This will avoid having to
        // change your code too much if the XML structure or data evolves
        // and allows you to manipulate a native JavaScript object.
        //
        // You can also use the UWA.Json.xmlToJson method to convert your document to a JSON object.
        //
        // In this example we choose to use document selector methods to demonstrate
        // simple usage of those methods.
        //

        var rootNode = xml.documentElement,
            currentMessage ={
                "message": rootNode.getElementsByTagName('message')[0].firstChild.nodeValue,
                "from": rootNode.getElementsByTagName('from')[0].firstChild.nodeValue,
                "preffix": rootNode.getElementsByTagName('to')[0].getAttribute('prefix'),
                "to": rootNode.getElementsByTagName('to')[0].firstChild.nodeValue
            };

        widget.setBody([
            {
                tag: 'h2',
                'class': 'messageFrom',
                text: 'Message from ' + currentMessage.from
            },
            {
                tag: 'div',
                'class': 'messageTo',
                text: 'To:  ' + currentMessage.to
            },
            {
                tag: 'p',
                'class': 'messageText',
                text: currentMessage.message
            },
        ]);
    }
};

// widget.onLoad() is fired when the app is fully loaded
// or when the preferences are validated
widget.addEvent('onLoad', MyWidget.dataInit);

Manipulating an XML document is not as easy as manipulating a JSON object. That's why, for complex XML documents, you may want to use the <UWA.Json.xmlToJson> method that converts an XML Document to a JSON Object.

getFeed request

RSS and Atom feeds have become a common way of sharing not only regular news items, but also other recurring data. Because of the many available formats and versions in the wild, they might not be easy to process directly.

UWA provides the UWA.Data.getFeed method, which facilitates the process of getting and parsing a feed, by turning it into the special JSON Feed Format.

Here is an example for processing entries from the Netvibes blog using UWA.Data.getFeed method:

var MyWidget = {

    dataInit: function () {
        UWA.Data.getFeed(
            'http://blog.netvibes.com/rss.php',
            MyWidget.dataProcessor
        );
    },

    dataProcessor: function(feed) {

        var content = UWA.createElement('div', {
                html: {
                    tag: 'p',
                    text: 'The feed for "' + feed.title + '" contains ' + feed.items.length + ' entries.',
                }
            }),

            // Create items list
            items = UWA.createElement('ul', {
                'class': 'items'
            }).inject(content);

        // Pupulate items list
        feed.items.forEach(function (item, index) {

            items.addContent({
                tag: 'li',
                'class': 'item',
                html: {
                    tag: 'a',
                    text: item.title,
                    href: item.link
                }
            });

        });

        // Inject content into widget body
        content.inject(widget.body);
    }
};

// widget.onLoad() is fired when the app is fully loaded
// or when the preferences are validated
widget.addEvent('onLoad', MyWidget.dataInit);

More info about the JSON Object Feed format is available in the UWA.Data.getFeed documentation.

Advanced requests

The previous methods are for simple Ajax requests. For more advanced usage, you should use UWA.Data.request directly instead of the getText/getJson/getXml/getFeed aliases.

  • This method should be used if you want to fine tune your Ajax request.
  • The method's first argument is the URL of the data source.
  • The second argument, named request, is a JSON-like object.

Here are the available settings for the request object:

Options Description Default value method GET, POST (in uppercase!) GET data GET or POST params as object {} type json, xml, text, html text cache seconds of proxy caching undefined timeout sets your request timeout in ms 25000 onComplete defines the onComplete callback undefined onTimeout defines the onTimeout callback throw onFailure defines the onFailure callback throw authentication the auth object. See doc undefined headers the headers object. See doc undefined See UWA.Data.request documentation for more options and detailed documentation.

Here is an example with almost all possible options:

UWA.Data.request('http://api.example.org/request', {

    // Send parameters as POST
    method: 'POST',

    // Response should be json object
    type: 'json',

    // Force UWA proxy usage
    type: 'ajax',

    // Cache result for 3600 seconds on proxy side
    cache: 3600,

    // Handle request response
    onComplete: function () {
        // do your data processing here
    },

    // Handle request error
    onFailure: function () {
        // do your data error here
    },

    // Custom Headers
    headers: {
        customHeader: 'myCustomHeaderValue'
    },

    // Custom HTTP authentication
    authentication: {
        username: 'john',
        password: 'doe'
    },

    // Custom Parameters
    data: {
        myString: 'I like Beer',
        myArray: [1, 2 , 3],
        myObject: {
            name: 'Beer',
            weight: '1.2lb'
        }
    }
});

The above example will produce following request:

> Request URL: http://john:doe@api.example.org/request
> Request Method: POST
>
> Request Headers
>  Content-Type: application/x-www-form-urlencoded; charset=UTF-8
>  X-Requested-Method: POST
>  X-Requested-With: XMLHttpRequest
>  customHeader: myCustomHeaderValue
>
> POST Data
>  myString: "I like Beer"
>  myArray[0]: "1"
>  myArray[1]: "2"
>  myArray[2]: "3"
>  myObject[name]: "Beer"
>  myObject[weight]: "1.2lb"

Why should you use UWA.Data.request rather then UWA.Ajax.request ?:

UWA.Data.request provides a Cross-domain AJAX solution that allows you to fetch data through Ajax calls even if your app is not on the same domain than your request. That is why you must not use UWA.Ajax methods but the UWA.Data.request method to perform such requests.