Page Navigation in Windows 8 JavaScript Apps | codefoster

Page Navigation in Windows 8 JavaScript Apps

Thursday, June 21, 2012 12:31 PM 5

I’d like to talk a bit about navigating in Metro apps using HTML/JavaScript. There are a few options for doing so, and as you probably know whenever there’s more than one way to do things, then you the developer have power but not necessarily clarity.  The latter is what I hope to offer here.

First of all, the HTML/JavaScript that Metro apps are based on are identical to the HTML/JavaScript that websites are based on. It is entirely based on the standards. This is good because proprietary things are bad - generally speaking. This means that you can navigate exactly like you do in websites, but don’t. I’ll explain why not.

So you could navigate from default.html to page2.html like this…

<a href="page2.html">link to page 2</a>

But again… you should usually do this. Doing so changes the “top level document location”. This navigation looks something like this…

Performing a top-level navigation.

Where the user is no longer on the default.html page. For websites, it’s just fine to jump around by navigating the top level like this because you’re usually not too concerned about state, but in a full-fledged application, you usually are concerned with state and you can make your life easier by using the built-in navigation features that are provided by the VS2012 templates.

When you use the navigation functionality, a navigation looks more like this…

Navigating to page2.html the recommended way.

Notice that the user is still on default.html, but the contents of the second page have simply been loaded into what is called the contenthost. Now, if you loaded a bunch of script and styles on default.html and even declared some variables and set some state, you still have all of that, even though to the user it appears that you’ve navigated to a second page.

Implementing this is pretty straight-forward. Follow these steps…

  1. Get the navigate.js script file that comes with the Navigation Application project template in VS2012. You can either start with the Navigation Application project template and notice that navigate.js is already in your js folder, or you can create a throw-away Nav project and steal that file.
    image
  2. Reference the navigate.js from your default.html file…
    image
  3. Add a contenthost to your default.html file
    image

And that’s it. After this has been implemented, then you are free to do things in your JavaScript like this…

WinJS.Navigation.navigate("/pages/page2/page2.html");

And you have the chance to pass some parameters without having to resort to query string parameters which can be cumbersome and restricting. To do this, you can pass a second parameter to the navigate function like this…

WinJS.Navigation.navigate("/pages/page2/page2.html", myDoohicky);

…where myDoohicky can be any JavaScript object you want.

Now, when might we actually perform this navigation? Well, in many cases it will be on some user action. For instance, let’s say the user is going to click a button and we want to navigate them to page2.html. Let’s see what that would look like…

HTML

<button id="myButton">go to page2</button>

JavaScript

ready: function (element, options) {
    document.querySelector("#myButton").onclick = function (args) {
        WinJS.Navigation.navigate("/pages/page2/page2.html", "test value");
    };
}

Now let’s look at a bit more pragmatic example. Let’s say we are working in a grid (WinJS.UI.ListView technically) and when the user touches one of the tiles, we want to navigate to a second page with more details about that element.

This can be wired up much like the simple button example above, but likely the elements in our grid are data bound from some list that we have. In that case, perhaps the easiest way to implement this is by adding a function to the list and then bind the click function just like any of the data elements are bound. Here’s an example of that…

HTML

<div id="headertemplate" data-win-control="WinJS.Binding.Template">
    <div>
        <p data-win-bind="innerText:firstLetter"></p>
    </div>
</div>
<div id="template" data-win-control="WinJS.Binding.Template">
    <div data-win-bind="onclick:clickFunction">
        <img class="img" data-win-bind="src:imageUrl" />
        <p class="name" data-win-bind="innerText:title"></p>
    </div>
</div>
<div id="list" data-win-control="WinJS.UI.ListView"></div>

JavaScript

ready: function (element, options) {
            
    var titlesListGrouped = new WinJS.Binding.List().createGrouped(
        function (i) { return i.title.charAt(0).toUpperCase(); },
        function (i) { return { firstLetter: i.title.charAt(0).toUpperCase() }; }
    );

    var list = q("#list").winControl;
    list.itemDataSource = titlesListGrouped.dataSource;
    list.itemTemplate = q("#template");
    list.groupDataSource = titlesListGrouped.groups.dataSource;
    list.groupHeaderTemplate = q("#headertemplate");

    WinJS.xhr({ url: "http://odata.netflix.com/Catalog/Titles?$format=json&$top=200" })
        .then(function (xhr) {
            var titles = JSON.parse(xhr.response).d;
            titles.forEach(function (i) {
                var item = {
                    title: i.ShortName,
                    imageUrl: i.BoxArt.LargeUrl,
                    clickFunction: function(args) { WinJS.Navigation.navigate("/pages/page2/page2.html", item); }
                };
                item.clickFunction.supportedForProcessing = true;
                titlesListGrouped.push(item);
            });
        });

}

Now, there’s a lot going on in the JavaScript file there, so let me break it down for you. First of all, I pulled this example from another post I did on creating a Netflix browser app utilizing their OData feed. If you want to know what’s going on with the call and the data binding, go check that out.

I added to it though. I changed what happens in the forEach loop. The reason I did is to illustrate how to bind a function to like you bind any other data property. Look in the HTML at the div just below the one with the id of “template”. I’m binding the onclick attribute to the clickFunction. That clickFunction is what I created in the forEach loop of the JavaScript. Notice, though, that there’s one funny thing we need to do to it. Since we are using this in the HTML it could be exploited and so we turn on strictProcessing for our app and that requires us to set supportedForProcessing on any functions that we are going to call from the HTML. So, we set that to true for our function and we’re good to go.

I hope this brings the concept home for you. If you have questions, leave a comment below and I’ll be glad to try to help.

Comments

  • gravatar SimpleScripts

    Fantastic blog. Your links in this are wonderful. I went via all this and I very many thanks for your advice.

    Jul 2012

  • gravatar mtilchen

    Thanks for this helpful post. I found the PageControlNavigator control provided by MS to be a bit lacking and created an abstraction for page navigation in Motown, a library for helping you write Metro style apps in HTML/JS/CSS. This post explains why I chose to do so and how you can use the advanced navigation features to do some nice things that you cannot do with the out-of-the-box MS code.

    labs.vectorform.com/.../motown-easy-javascript-...

    You can try out Motown by going to the GitHub repo and cloning or by downloading a stable release.

    http://github.com/mtilchen/motown

    Jul 2012

  • gravatar Kamran

    I wanted to point out to people using this as a resource: the recommend way to handle list view clicking is through the "iteminvoked" event. See the C#/HTML sample: "HTML ListView essentials sample": code.msdn.microsoft.com/.../ListView-basic-usag...

    I was confused because I had implemented a custom IListDataAdapter and was getting raw JSON from my service. I wondered how I could attach a click function to those objects in the data source... but that's not the right way to go about it (since the data source can be reused anywhere). What I had done at first was pass in an optional mapping function (when the data source was new'd up) to convert the raw JSON objects to a view model; but luckily my case was simple so once I found out I only had to switch my implementation to use iteminvoked event instead.

    Sep 2012

  • gravatar Ian

    How do you actually grab the value of myDoohicky on the receiving end?

    Dec 2012

  • gravatar prasanna

    @ Ian you can get the value of myDoohicky using this code..
    var myval = WinJS.Navigation.state;

    Feb 2013

Write a comment