Networking in WinJS and WinRT
A large number of client-side apps require access to data provided over the Web, among them e-mail, photo browsing, social networks, music playback, document syncing, and multiplayer games. If you can name a popular app built in the past decade, chances are that it makes use of data accessed over a network. Toward that end, Windows Store apps have several ways to access data over the network, including the most basic: the XMLHttpRequest object.
XMLHttpRequest (XHR) is the name of the object that sparked the AJAX10/Web 2.0 revolution in 2005 (although the object has been part of Internet Explorer since version 5.0, released in 1999).11 It provides for downloading data using HTTP. The xhr function provided with WinJS is an XMLHttpRequest wrapper that only requires the URL from which to retrieve data:
// postsPage.js
...
WinJS.UI.Pages.define("/pages/postsPage/postsPage.html"
, { ready:function
(element, options) {// download the feed
this
.feed = options.feed;var
pageTitle = element.querySelector(".pagetitle"
); pageTitle.innerText =this
.feed.title;this
.section = document.querySelector("section[role=main]"
);this
.section.innerHTML ="<p>downloading...</p>"
;// download using XMLHttpRequest by creating a promise and
// telling it what to do when it's done
// the long way
var
xhrPromise = WinJS.xhr({ url:this
.feed.url }); xhrPromise.done( processPosts.bind(this
), downloadError.bind(this
));// the short way (recommended)
WinJS.xhr({ url:this
.feed.url }). done(processPosts.bind(this
), downloadError.bind(this
)); }, });
Before downloading the feed data, we stash the feed object into a property associated with this instance of the postsPage page control, set the page title using the feed’s title, and set a progress indicator for download. The reason we let the user know that we’re downloading is because there’s no telling how long it’s going to take to do the actual download. Further, when we call the xhr function, passing in the URL for the feed, the result is not returned to us synchronously, blocking UI updates until the data winds its way back from some random server on the Internet. Instead, the return from xhr is a promise.
In fact, all asynchronous functions in WinJS (and in the WinRT) return an instance of WinJS.Promise, which represents results to be provided at some time in the future. The Promise object exposes the done method, which takes three functions as optional arguments: one for success, one for failure, and one for progress.
Upon success, our processPosts method is called:
// process using XMLHttpRequest
function
processPosts(request) {// clear the progress indicator
this
.section.innerHTML =""
;// parse the RSS
var
items = request.responseXML.querySelectorAll("item"
);for
(var
i = 0, len = items.length; i < len; i++) {var
item = items[i];var
parent = document.createElement("div"
); appendDiv(parent, item.querySelector("title"
).textContent,"postTitle"
); appendDiv(parent, item.querySelector("pubDate"
).textContent,"postDate"
); appendDiv(parent, item.querySelector("description"
).textContent,"postContent"
);this
.section.appendChild(parent); } }function
appendDiv(parent, html, className) {var
div = document.createElement("div"
); div.innerHTML = toStaticHTML(html); div.className = className; parent.appendChild(div); }
This code is pretty standard HTML DOM manipulation and XML processing code familiar to any experienced JavaScript programmer, creating div elements as we did earlier in the chapter. The only thing that’s unique to Windows Store apps is the call to the toStaticHTML method. This call is specifically for when we have random HTML from an unknown source. By default, when setting the HTML of an element, the HTML engine will throw an exception if it finds a piece of dynamic HTML such as a script tag. The toStaticHTML call strips out any dynamic HTML it finds, rendering the content unable to take over your app.12
In the event that there’s an error, we let the user know:
function
downloadError(feed) {this
.section.innerHTML ="<p>error</p>"
; }
With this code in place as well as some styling in postsPage.css, our app is finally starting to rock, as you can see in Figure 1.23.
Figure 1.23. Showing the contents of an RSS feed using WinJS.xhr
At this point, there are a few nits in our networking code that we might like to work through. For example, Brandon puts a summary of his posts in his feed’s description field, whereas I put my entire set of content in there (both approaches are valid). Also, the XML parsing code we’ve written is specific to RSS,13 whereas most blogs these days support Atom.14 Luckily, because RSS and Atom are so prevalent on the Internet, the WinRT library provides a set of types for dealing with feeds of both syndication formats:
WinJS.UI.Pages.define("/pages/postsPage/postsPage.html"
, { ready:function
(element, options) { ...// download using WinRT
var
syn =new
Windows.Web.Syndication.SyndicationClient();var
url =new
Windows.Foundation.Uri(this
.feed.url); syn.retrieveFeedAsync(url).done( processPosts.bind(this
), downloadError.bind(this
)); }, });
In this code, we’ve replaced the use of the xhr function with the WinRT SyndicationClient and Uri types from the Windows.Web.Syndication and Windows.Foundation namespaces, respectively. Like the xhr function, the retrieveFeedAsync function is asynchronous, returning a promise that works exactly like every other async function in WinJS or WinRT. In our success handler, we handle a list of posts instead of raw XML:
// process using WinRT
function
processPosts(request) {// clear the progress indicator
this
.section.innerHTML =""
;// iterate over the items
for
(var
i = 0, len = request.items.length; i < len; i++) {var
item = request.items[i];var
parent = document.createElement("div"
); appendDiv(parent, item.title.text,"postTitle"
); appendDiv(parent, item.publishedDate,"postDate
"); appendDiv(parent, item.summary.text,"postContent
");this
.section.appendChild(parent); } }
The updated networking code is a little smarter about where Brandon keeps all of his content, as Figure 1.24 shows.
Figure 1.24. Showing the contents of an RSS feed using the WinRT SyndicationClient
And not only is the WinRT smart about Brandon’s feed and RSS versus Atom, but you’ll notice that Visual Studio 2012 is smart about the WinRT. At no time did I need to add a WinRT reference or do anything else special to access a WinRT type or namespace. In fact, if you start typing “Windows.” inside Visual Studio 2012, you’ll see that it knows all about it (see Figure 1.25).
Figure 1.25. Visual Studio 2012 knows WinRT!
You’ll see a great deal more of WinRT throughout this book, but I encourage you to dig around the Windows namespace on your own; there’s a lot of good stuff in there.