What You’ll Learn This Hour:
• Making calls to a remote server using WinJS
.
xhr
• Parsing RSS and Atom feeds
• Passing different options into WinJS
.
xhr
• Downloading many types of data
• Forcing an update even if a request is cached locally
Working with local data is needed, but many times apps need to pull data from the Internet. During this hour, we show see some of the ways to connect to outside web services. We explore different ways to consume remote data and how to bring that data into apps.
We work on an RSS Reader app that reads in both RSS content and Atom content. Each post is parsed and stored in an array bound to a ListView
. We start with the Split App project and modify the data.js
to utilize remote data instead of the local data we have seen so far.
A common example of pulling external data is to consume RSS (Really Simple Syndication) feeds. Atom is another feed format that has the same purpose of RSS but is formatted a little differently. Both file formats are XML.
In this first section, we write a RSS reader app for a single web site. After better understanding how to make a compliant Windows Store app, we cover how to make a full-fledged RSS aggregator app during Hour 19, “Making an App: RSS Aggregator.”
We start with the Split App project template and make modifications to add the functionality we seek.
Now we can work on actually bringing in remote data to populate the ListView
control. We replace the sample data with an actual feed and its corresponding posts.
Now it is time to put the finishing touches on the app so that the data we worked so hard to obtain is visible to the user. The project should run in its current state, but many items aren’t bound correctly, and it doesn’t look good.
If we run the app now, we can see on slower machines that the list of posts is rendering in an odd manner. The top item is flickering. This is a performance problem. ListView
is trying to update what we are giving it, and we are giving it the entire contents of the posts for every item in the list. This is just too much data—and it is wasted because we are letting the user see only the first couple of lines of the post anyway. To correct this, let’s add another value to the object we are pushing into the feedPosts
array inside the getItemsFromXml
function in data.js
:
feedPosts.push({
group: feed,
key: feed.key,
link: postLink,
title: postTitle,
author: postAuthor,
postDate: postDate,
content: staticContent,
shortContent: toStaticHTML(content.textContent.substring(0, 400))
});
The shortContent
property contains the first 400 characters of the content of the post. We can just truncate it without worrying about being in the middle of markup because we are wrapping it in toStaticHTML
. Truncating at a nonregular text location smoothes things over. Now that we have created the shortContent
property, we need to bind it, instead of content, to the innerHTML
attribute inside split.html
:
<h4 class="item-description"
data-win-bind="innerHTML: shortContent"></h4>
Now when we run it, we see the same amount of data that we did before, although we are binding much less data to the individual items. This has removed the performance issue for this simple app. We cover how to handle dynamic loading instead of waiting on all items during Hour 19.
At this point, we have completed an RSS reader that retrieves an RSS (or Atom) feed and displays all the posts from that feed in a split view. We modified the built-in data.js
template to change from using static data to using dynamic data. In this example, we force the data to load before the page loads. We could load some preloaded data first and then refresh with the latest info after the page loads. We could just display nothing but an updating and progress bar and then display the data after it is loaded. We could display static data and, after successfully loading, store the updated data as the new static data we will load next time the app is run. During Hour 11, “Storing Data Locally,” we explored how to store application data. This would be a great time to do just that. Figure 12.1 shows the results of the RSS Reader.
We use this app as a basis to make a more robust app when we are done with the next part of the book, “Making It a Windows Store App.” After we gain the knowledge needed to make our apps utilize the great features that Windows 8 offers, we can create some compelling apps. We convert this modest RSS Reader into a full-fledged Windows Store app during Hour 19.
WinJS.xhr
The WinJS
.
xhr
function simply calls the XMLHttpRequest
function and wraps it in a promise. With IE 10 and Windows Store apps, we can download many different types of data. We can get the content length of the request by using an arraybuffer
:
WinJS.xhr({ url: "http://www.microsoft.com", responseType: "arraybuffer" })
.done(function (result) {
var arrayResponse = result.response;
var dataview = new DataView(arrayResponse);
var ints = new Uint32Array(arrayResponse.byteLength / 4);
xhrDiv1.innerText = "Array is " + ints.length + " uints long";
});
We can download binary data into a blob
:
//Video being displayed from: http://www.w3.org/2010/05/video/mediaevents.html
WinJS.xhr({
url: "http://media.w3.org/2010/05/sintel/trailer.mp4",
responseType: "blob"
})
.done(function (request) {
var videoBlob = URL.createObjectURL(request.response, { oneTimeOnly: true });
var video = document.getElementById("vid");
video.src = videoBlob;
},
function (err) {
video.innerHTML = err;
});
We can download XML into a document by using a responseType
of document
. We can download json
into a string:
WinJS.xhr({
url: "http://api.geonames.org/citiesJSON?" +
"north=44.1&south=-9.9&east=-22.4&west=55.2&lang=de&username=demo",
responseType: "json"
})
.done(
function (request) {
var geo = JSON.parse(request.responseText);
var geoname = geo.geonames[0];
xhrDiv5.innerHTML = geoname.toponymName + ", " + geoname.countrycode;
xhrDiv6.innerHTML = request.responseText;
});
The default responseType
is text
, which is what we used in Hour 10 and in the previous RSS Reader.
The code can be found under Hour12WinJSxhrExample
. Figure 12.2 shows the output of the program.
We have seen that we can use url
and responseType
as options to be passed to the xhr
function. The rest of the options include those shown in Table 12.1.
Sometimes we want to set a timeout on a request. XMLHttpRequest
enables us to directly set a timeout, but WinJS
.
xhr
does not. However, we can still set a timeout by doing so on the WinJS
.
Promise
object. WinJS
.
Promise
.
timeout
cancels the request if it does not complete within the allotted timeout period:
WinJS.Promise.timeout(2500,
WinJS.xhr({ url: "http://www.microsoft.com" }).then(function () {
var text = request.responseText;
}));
This code waits for the request to finish for 2.5 seconds. If it doesn’t return in that time, it cancels the request.
WinJS
.
xhr
can cache results, to help with performance. But sometimes we want to resend the request to get the latest data, not the cached data. This is done by adding an HTTP header to make sure the request is resent, even it was previously cached. The headers option can have the If-Modified-Since
property set to force the request to reach out to the server. For example:
var btnReGet = document.getElementById("btnReGet");
btnReGet.addEventListener("click", reloadMicrosoftdotcom, false);
reloadMicrosoftdotcom();
function reloadMicrosoftdotcom(evt) {
WinJS.xhr({
url: "http://www.microsoft.com",
headers: {
"IF-MODIFIED-SINCE": "Mon, 27 Mar 1972 00:00:00 GMT"
}
})
.done(function (result) {
// Report download.
xhrDiv5.innerText = "Downloaded the page at " + new Date();
});
}
We looked at only WinJS
.
xhr
during this hour, but there are many other ways to talk to remote data. The key to most of them is to use the XMLHttpRequest
function. We didn’t discuss application caching, but typically when we pull information from the cloud, it is best to cache items so that if users can’t connect to the Internet, they can still use the app. People expect apps to work even if they have no Internet connectivity; we need to make sure that our apps are caching items where appropriate when working with remote resources. Information can be stored in the local application storage, or in some cases, it may make sense to use the Application Cache API to enable offline mode of the app. More information on the Application Cache API can be found at http://msdn.microsoft.com/en-us/library/windows/apps/hh767297
.
Another topic that wasn’t discussed was Single Sign-On. Microsoft has the Live SDK, which enables apps to connect to the user’s SkyDrive (beyond what is allowed with Application Data Roaming Storage). More information on SkyDrive integration can be found at http://blogs.msdn.com/b/windowsappdev/archive/2012/03/14/bring-single-sign-on-and-skydrive-to-your-windows-8-apps-with-the-live-sdk.aspx
.
We didn’t discuss WebSockets because it is beyond the scope of the book, but Microsoft has an excellent example, listed below. We also didn’t discuss connecting to third-party services such as Facebook or Twitter. Microsoft provides a great example on how to do that. Be sure to check out the Windows SDK samples:
• Connecting with WebSockets sample
• Web authentication broker sample
• XHR, handling navigation errors, and URL schemes sample
We spent most of the chapter creating an RSS and Atom reader app. We discussed how to utilize the XMLHttpRequest
function, called by WinJS and wrapped in a promise. We covered how to parse the XML data we retrieve from the feeds. We also explored how to bind data to our ListView
from an outside source. We discussed some of the options we can pass into the WinJS
.
xhr
function and the different types of data we can retrieve by using it. We finished the hour discussing how to send a particular value through the HTTP header information to force a reload and ignore any caching.
Q. How do I use WinJS.xhr
to post data?
A. Posting data is straightforward, but it does require the type
to be set along with the Content-type
inside the headers
property bag. Of course, it needs any data you are actually posting:
WinJS.xhr({
type: "post", user: username, password: password,
url: "https://api.someservice.com/Users/" + username + "/Friends.json",
headers: { "Content-type": "application/x-www-form-urlencoded" },
data: data
}).then(success, error);
Q. What application capabilities do I need to set to access the Internet?
A. The default project templates in Visual Studio automatically set the Internet (Client)
capability.
Q. How can I update a ListView
control after dynamically loading remote data?
A. If a data array wasn’t bound to the ListView
before it rendered, a call to listView
.
ForceLayout
();
forces the ListView
control to rerender, in which case it read in the data bound at that point. The main purpose of the ForceLayout
method is to handle after a view mode change or after displaying the ListView
control after it was hidden using display
:
none
.
1. Because WinJS.xhr doesn’t have a timeout function, we need to use WinJS.Promise.timeout
to set a timeout on a request. True or false?
2. How do you turn a JSON string from a service into an actual JavaScript object?
3. How many seconds after launch does an app have to become responsive before the system terminates it?
4. What is the only mandatory property in the options property bag that must be set in WinJS
.
xhr
?
1. True. XMLHttpRequest
has a timeout setting, but it isn’t available through WinJS.xhr
. However, we can use the WinJS
.
Promise
.
timeout
to cancel the request if it goes too long.
2. The JSON.parse
function takes a JSON string and returns a JavaScript object:
var obj = JSON.parse(someJSONString);
3. An app has 15 seconds to load after it has launched. If it takes longer than that to complete the app load process, Windows terminates the app. To pass certification, an app can take only 5 seconds to load.
4. The only required value to be passed to WinJS
.
xhr
is the url
value. All other properties have defaults.
1. Add error handling to the RSS Reader app. If an invalid URL is used, make the system handle it gracefully instead of throwing an exception.
2. Format the publish date to be a better display date in the RSS Reader app.
3. Modify the RSS reader app to load static data when it first loads. Then update the ListView
(option 1: listView
.
ForceLayout
();
option 2: call the split page’s ready
event directly) to change the contents of the ListView
control when the updated data is available. At the same time, the data is retrieved; store the data to local app data storage. When the app loads, it should pull from this static data, if present.
4. Connect to a third-party web service (either XML or JSON) and consume the data in your app.
18.118.28.179