Using XMLHttpRequest from QML in Cascades to access web services

by Developer on ‎04-08-2013 10:23 AM (7,141 Views)

Passing data back and forth between QML and C++ is (relatively) quite a lot of work and complicates apps.  A lot of the performance benefit is lost in converting data types (particularly JSON -> QVariant<List/Map> structures -> JavaScript® Objects is a bit crazy if you ask me). If you don't need to do much processing of the data then simply accessing the network from JavaScript is much more convenient.  If you're accessing a webservice that uses JSON then it's ideal.


The good news is that this does work.  Only asynchronous requests are supported and if you don't check that the request readyState is DONE before querying the request status then you get "Invalid state" errors that are just attributed to the relevant QML file rather than any specific bit of code. Below is some skeleton code for a POST request but others work similarly:

function submitPost(postString) {
                var request = new XMLHttpRequest();
                request.onreadystatechange=function() {
                    // Need to wait for the DONE state or you'll get errors
                    if(request.readyState === XMLHttpRequest.DONE) {
                        if (request.status === 200) {
                            console.log("Response = " + request.responseText);
                            // if response is JSON you can parse it
                            var response = JSON.parse(request.responseText);
                            // then do something with it here
                        else {
                            // This is very handy for finding out why your web service won't talk to you
                            console.log("Status: " + request.status + ", Status Text: " + request.statusText);
                // Make sure whatever you post is URI encoded
                var encodedString = encodeURIComponent(postString);
                // This is for a POST request but GET etc. work fine too
      "POST", "https://<your_service_endpoint_here>", true); // only async supported
                // You might not need an auth header, or might need to modify - check web service docs
                request.setRequestHeader("Authorization", "Bearer " + yourAccessToken);
                // Post types other than forms should work fine too but I've not tried
                request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
                // Form data is web service dependent - check parameter format
                var requestString = "text=" + encodedString;

The other good news is that JSON.parse() and JSON.stringify() are also available (so please don't use eval). 


While researching this I discovered that *apparently* adding elements to a ListModel in JavaScript is very slow compared to C++, so you don't want to do it with very large numbers of objects.  That said, there are pure QML/JS Twitter® clients with decent performance on slow old Symbian devices.  I can't confirm how slow because:

"Currently, you can use the profiler only for C++ code. You can't use the profiler for QML or JavaScript code." 


If you've only got fairly small amounts of data to pass around, particularly if it's JSON, then this is a more convenient way of accessing web services with minimal performance impact.  Being able to do this kind of simple web-connected app entirely in QML/JS was part of the original vision for the language - at least for prototyping.