Navigation
The idea of navigation between pages is not new. In fact, it’s the Hypertext part of the Hypertext Markup Language (HTML). As the user clicks on links (or HTML elements with onclick handlers), we often want to bring up a whole new page of data, controls, images, and so forth. In the browser, when this happens, we most often pull down a new page, blanking out the screen and clearing out all of the current state. While we can navigate in a Windows Store app in the same way we can in the browser, we generally prefer to use the navigation service built into WinJS, which gives us much greater control over the UI as we move from page to page and allows us to keep the app state we build up over time, like we can with our list of feeds.
However, before we navigate anywhere, we need somewhere to navigate to. And for that, you’ll want to right-click on the pages folder in your project from the Solution Explorer and add a new folder for your page using Add | New Folder, calling it postsPage. This will hold the files for your new page, which you can add to that folder by right-clicking and choosing Add | New Item and then choosing the Page Control item from the JavaScript | Windows Store category. What you’ll see looks like Figure 1.19.
Figure 1.19. The Add New Item dialog for Windows Store apps
Each of the item templates in the Windows Store category produces a set of three files—an HTML file, a CSS file, and a JavaScript file—that compose a page control suitable for use in WinJS navigation. The Page Control template creates a blank page control. The other three templates help you implement shell contracts, which you can read all about in Chapter 9, “Shell Contracts.”
Entering the name, such as postsPage.html, and pressing Add creates the three new files for our page control, as Figure 1.20 shows.
Figure 1.20. A new page control added to a Windows Store app
That’s all we need to do to get a page ready to be a navigation target—the question is, how do we perform the navigation? In the case of the ListView, we need to let the ListView know we’d like to be notified when an item is invoked, as shown in the code on the following page.
<!DOCTYPE html>
<!-- home.html -->
<html>
<body>
...<section aria-label="Main content" role="main">
<div data-win-control="WinJS.UI.ListView"
data-win-options="{
itemDataSource: feeds.dataSource,
itemTemplate: select('#feedTemplate'),
selectionMode: 'none',
oniteminvoked: feedInvoked}">
</div>
</section>
</div>
</body>
</html>
Remember when we added the ListView control to the design surface in Blend? All that did was add the div and set the data-win-control and data-win-options attributes. The div represents the WinJS control in the HTML DOM, and WinJS.UI.ListView is the name of the JS constructor function (which you can learn all about in Chapter 2). You may also recognize the itemDataSource and itemTemplate settings we set in the Blend properties panel.
You don’t have to use Blend to edit those properties; your favorite text editor will do. In this case, we need to set the selection mode to none (we don’t want selection—we want invocation), and we set the name of the handler we want to call when the user invokes an item. The handler is implemented in the corresponding JavaScript file:
// home.js
(function
() {"use strict"
; window.feeds =new
WinJS.Binding.List([ { title:"Brandon Satrom"
, url:...
}, ... ]);// mark the event handler as safe for declarative use
window.feedInvoked = WinJS.UI.eventHandler(function
(e) {// navigate to the page to show the feed's posts
var
feed = feeds.getAt(e.detail.itemIndex); WinJS.Navigation.navigate("/pages/postsPage/postsPage.html"
, { feed: feed }); });...
})();
The feedInvoked handler is wrapped in the eventHandler function, which marks it as safe for use from the data-win-options in the home.html file. This is a security measure to make sure that HTML downloaded from the Internet doesn’t get to hijack your apps.
The implementation of the feedInvoked handler reaches into the detail property of the event object to find the index of the item that was invoked. This feed object is passed to the postsPage using the navigate method. The navigation services of WinJS then loads the postsPage and passes the feed object to the ready function via the options parameter:
// postsPage.js
(function
() {"use strict"
; WinJS.UI.Pages.define("/pages/postsPost/postsPage.html"
, { ready:function
(element, options) {// TODO: do something with the feed object the user invoked
var
feed = options.feed; }, }); })();
Now that we have an invoke handler set up on the ListView, clicking on a feed title on the home page (Figure 1.21) brings us to the page we’ve built to show the feed’s posts (Figure 1.22).
Figure 1.21. Triggering the invoke event on a ListView control
Figure 1.22. Navigating to a page control using the WinJS navigation service
By now, you may have noticed that while the Back button element is present in home.html, it’s not showing in Figure 1.21, even though it is showing in the postsPage.html shown in Figure 1.22. That’s because the navigation support in the templates is smart enough to know that there is no history before the home page to go back to, which is why it only shows the Back button where there is a “back” to go back to. Further, you can’t see this, but the templates also support the Back and Forward keystrokes that the browser supports (like Alt+Left Arrow and Alt+Right Arrow).
All of this means that you can write your pages as page controls and pass objects around, letting the navigation support in the templates do the heavy lifting.
Of course, we’re not done with even the basic functionality of our RSS Reader yet because we still haven’t downloaded the posts from the selected feed. To do that, we’ve got to write a little networking code.