Condividi tramite


How to port a Windows Runtime app using JavaScript to Windows Phone

[This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation]

This article describes a step-by-step process for porting your Windows Runtime app using JavaScript from Windows 8.1 to Windows Phone 8.1. The easiest way to do this is by creating a new Microsoft Visual Studio solution based on the Windows Phone template for Windows Runtime apps. You can reuse a lot of the code in your Windows Runtime app on Windows Phone, but some code will need to be revised. This article uses a concrete example of porting a specific Windows Runtime app to Windows Phone in order to demonstrate some of the major issues you can encounter.

Note  

  • If you want to reduce the amount of maintenance required for upkeep of your Windows Runtime app using JavaScript, it is suggested that you use the Universal project template in Visual Studio. The benefit is that you can reuse the exact same code files between the Windows and Windows Phone versions of your app. However, it does require more work up front to port an existing Windows Runtime app using JavaScript to the Universal template.
  • This walkthrough uses a published code sample as an example for how to port a Windows Runtime app to Windows Phone. You can download the entire code sample — both the Windows and Windows Phone version — from Code Gallery.

 

Prerequisites

Instructions

Step 1: Plan the Windows Phone version of a Windows Runtime app

Before you open Visual Studio and begin porting your app to Windows Phone, take a minute to survey the contents of your app. Broadly speaking, you can divide the files and artifacts within your app into two categories: content, layout, and form factor on one hand; data, resources, and business logic on the other. The data and business logic backing your Windows Runtime app can stay almost entirely the same; little or no change should be needed. This category includes items like XML data files or JavaScript libraries that perform data operations. The other category (content, layout, and form factor items) includes HTML pages, Cascading Style Sheets (CSS) files, and some visual assets. Those probably do need to be revised to some degree to fit the form factor of a Windows Phone device.

When considering which category a file in the project falls into, consider some of the following questions:

  • Is the file "platform-agnostic"? For example, could it be reused in a normal Web page running in the browser without any changes?
  • Can you reuse your visual assets? That is, can you reuse your store logo, splash screen, square tile, for the Windows Phone app?
  • Is your app designed for landscape or portrait orientation? Most users interact with their Windows Phone device in portrait orientation, whereas most Windows apps are optimized for landscape. Also, unless your app is locked in one orientation, your app needs to react appropriately when the user turns the device.

One major issue you need to resolve is navigation. If your Windows Runtime app only has a single page without navigation, you can probably share the single HTML page between the two platform versions without too many revisions. However, if you use multiple page controls with a NavBar control or Hub control to switch between pages, you need to decide on a different navigation scheme for your Windows Phone Store app using JavaScript. Neither the Hub control nor the NavBar control are supported on Windows Phone (see WinJS API changes for Windows Phone 8.1).

If your Windows Runtime app uses a Hub control for layout or navigation, then you can use the new Pivot control on Windows Phone. Updating your HTML or JavaScript code to use the Pivot control is very easy (see below). Keep in mind that the Pivot control, unlike the Hub control, does not provide a onheaderinvoked to respond to user interaction with the headers of PivotItem objects. If your app's navigation experience relies on this functionality, you need to reconsider how your app switches between pages.

If your Windows Runtime app uses a NavBar control for navigation, then you'll need to make a choice. You could replace the NavBar control with an AppBar and then add code to your AppBar to handle button clicks. However, keep in mind that the AppBar on Windows Phone can only display four buttons in the minimized app bar. If your app has more than four pages, the additional page navigation buttons need to be displayed as text (when the app bar is fully shown).

The AppBar is also a good place for users to access the settings page for your Windows Runtime app app (if your app has a settings page). The SettingsFlyout control is not supported on Windows Phone, so you should consider moving a link to your app's setting page to the AppBar.

Step 2: Create the new Windows Phone project in Visual Studio

You can create a new Windows Phone Store app using JavaScript by using Microsoft Visual Studio 2013 Update 2.

  1. Open Visual Studio.
  2. In Visual Studio, click File, New, Project.
  3. In the New Project dialog box, expand Templates, expand JavaScript, expand Store Apps, and select Windows Phone Apps.
  4. Select the type of project that you want to create. For the Building efficient Windows Store apps with JavaScript code sample referenced in this article, the Navigation App template is used.
  5. Enter a name for your project. Note   The final, published name for your app should probably be similar to your Windows Runtime app for Windows, but should clearly be targeted for Windows Phone. For example, Building efficient Windows Store apps with JavaScript becomes "Building efficient apps with JavaScript for Windows Phone."  

Step 3: Move platform agnostic files to the Windows Phone Store app using JavaScript project

There are many files that work without any change on Windows Phone 8.1. These files can be simply copied from your Windows project over to the Windows Phone project. Some examples are data files (like XML documents), resources, visual assets, HTML fragments that don't use Windows Library for JavaScript (WinJS) controls, or 'pure' JavaScript files.

To copy files from one Visual Studio solution to another solution

  1. If it's open, close the Windows Phone project in Visual Studio.
  2. In Windows Explorer, copy your files from the directory for the Windows version of the app to the corresponding sub-directory in the Windows Phone version of the app.
  3. Open your Windows Phone project in Visual Studio.
  4. In Solution Explorer, right-click the directory where you want to add the files and then choose Add, Existing Item.
  5. In the Add Existing Item dialog box, navigate to the directory for the Windows Phone project and select all of the files that you moved. Click Add after you've selected all of the items.

After adding your items to the Windows Phone project, you need to integrate them into your app. For JavaScript (*.js) and CSS (*.css) files, you need to add references to the files in the HTML in your app. For visual assets, you need to open the package.appxmanifest file and add the references to the copied visual assets (on the Visual Assets tab in the Manifest Designer).

For example, in the Building efficient Windows Store apps with JavaScript code sample, the data.js (/js/data.js), jquery-2.03.js (/js/jquery-2.03.js ), and searchLOC.js (/js/searchLOC.js) files can be moved directly into the Windows Phone project. They contain non-platform specific code or Windows Library for JavaScript features that are supported on both platforms.

Reference copied files in default.html of a Windows Phone project

    <script src="/js/searchLOC.js"></script>
    <script src="/js/data.js"></script>
    <script src="/js/jquery-2.0.3.js"></script>

Caution   HTML pages that you move over from the Windows project can contain references to the WinJS. These references need to be changed on Windows Phone to point towards the phone-specific versions of the library files.

 

<!-- WinJS references -->
<link href="//Microsoft.Phone.WinJS.2.1/css/ui-dark.css" rel="stylesheet" />
<script src="//Microsoft.Phone.WinJS.2.1/js/base.js"></script>
<script src="//Microsoft.Phone.WinJS.2.1/js/ui.js"></script>

Tip   If you use the Universal application solution for your Windows Runtime app using JavaScript, you can store platform-agnostic files within the Shared project. By doing so, you make the same files available to both the Windows and Windows Phone version of your projects.

 

Step 4: Move platform-specific files to the Windows Phone Store app using JavaScript solution

Your Windows Runtime app project probably contains two very important files: default.html and default.js (/js/default.js). These two files provide the "structure" framework that girds your app. (Although you can rename these two files in your app, in this article it is assumed that your existing Windows Runtime app uses these two files by the template default names.) The Windows Phone Store app using JavaScript project template in Visual Studio includes both default.html and default.js files, whose purpose is the same as their counterparts in the Windows project. This raises the obvious question: can you reuse those two files as-is without major refactoring? The answer is different for the each of the two files.

The default.html file provides several important functions for your app: it contains the global script and style references as well as providing a basic structure of your app's presentation and layout. As mentioned previously, the SCRIPT and LINK tags that reference the WinJS on Windows Phone are different. For Windows Runtime apps on Windows, default.html is a good place to create a NavBar control (for global navigation) and a host element for displaying different pages in your app. Windows Phone doesn't support the NavBar control, so if your default.html file contains a NavBar control you must change it to use a supported control (such as AppBar). It is not recommended that you reuse the default.html file from your Windows project in your Windows Phone project.

For example, in the Building efficient Windows Store apps with JavaScript code sample, the default.html file had to be refactored to use an AppBar control rather than a NavBar. This leads to two major changes. First, the location that each button points to is moved to the value attribute of the button that hosts the AppBarCommand control. Second, the first four AppBarCommand controls are placed in the 'global' section for the AppBar and all the AppBarCommand controls are placed in the "selection" section. This is because Windows Phone only displays the last four AppBarCommand controls declared in the "global" section.

Also note that the names of the individual pages has been shortened to one to two words. The smaller form factor of the Windows Phone device doesn't display long, wordy labels very well. You may need to shorten the labels on the navigation buttons in your app.

The default.html file for BuildingEfficientApps_Phone

<body>
    <div id="contenthost" data-win-control="Application.PageControlNavigator" data-win-options="{home: '/pages/home/home.html'}"></div>
    <!-- The global navigation bar for the app. -->
    <div id="navBar" data-win-control="WinJS.UI.AppBar">
        <button id="homeNav"
                value="/pages/home/home.html"
                data-win-control="WinJS.UI.AppBarCommand"
                data-win-options="{
                    icon: 'home',
                    label: 'Home',
                    section: 'global'
            }"></button>
        <button id="handlingErrors"
                value="/pages/handlingErrors/handlingErrors.html"
                data-win-control="WinJS.UI.AppBarCommand"
                data-win-options="{
                    icon: 'help',
                    label: 'Errors',
                    section: 'global'
                }"></button>
        <button id="chainedAsync"
                value="/pages/chainedAsync/chainedAsync.html"
                data-win-control="WinJS.UI.AppBarCommand"
                data-win-options="{
                icon: 'link',
                label: 'Async',
                section: 'global'
            }"></button>
        <button id="dispose"
                value="/pages/dispose/dispose.html"
                data-win-control="WinJS.UI.AppBarCommand"
                data-win-options="{
                icon: 'delete',
                label: 'Dispose',
                section: 'global'
            }"></button>
        <!-- 'Selection' controls -->
        <button id="scheduler"
                value="/pages/scheduler/scheduler.html"
                data-win-control="WinJS.UI.AppBarCommand"
                data-win-options="{
                icon: 'clock',
                label: 'Scheduler API',
                section: 'selection'
            }"></button>
        <button id="worker"
                value="/pages/worker/worker.html"
                data-win-control="WinJS.UI.AppBarCommand"
                data-win-options="{
                icon: 'repair',
                label: 'Web Worker API',
                section: 'selection'
            }"></button>
    </div>
</body>

The default.js file, unlike default.html, can probably be reused between the two projects. The default.js file provides the plumbing for the app's lifecycle. It contains the handlers for the WinJS.Application.onactivated and WinJS.Application.oncheckpoint events, as well as a call to WinJS.Application.start. All of those APIs are supported on Windows and Windows Phone.

Caution   One major difference between Windows and Windows Phone is the behavior of apps when the user navigates away from them. For example, when a user clicks the hardware's back button on a Windows Phone, the app continues running. Your app needs to account for this behavior in the handler for the WinJS.Application.onactivatedevent.

 

Managing app suspension and resumption in default.js

WinJS.Application.addEventListener("activated", function (args) {

    var activation = Windows.ApplicationModel.Activation;

    if (args.detail.kind === activation.ActivationKind.launch) {
        if (args.detail.previousExecutionState === activation.ApplicationExecutionState.running ||
            args.detail.previousExecutionState === activation.ApplicationExecutionState.suspended) {
            
            // The app has already been launched before, no need for set-up.
            return;
        }
    }
});

For example, in the Building efficient Windows Store apps with JavaScript code sample, the default.js file is mostly reused. Besides the code already mentioned, it contains the handler for the WinJS.Application.onerror event, which is supported on both Windows and Windows Phone. Some extra code needed to be added, though, to support navigation. The Windows Phone version of Building efficient Windows Store apps with JavaScript uses an AppBar control rather than a NavBar. The AppBarCommand controls in the AppBar needed event handlers assigned to their button click events to provide page navigation, as shown in the following code.

Wiring up navigation logic to an AppBar control in default.js of BuildingEfficientApps_Phone

// Add button-click event handlers to each of the AppBarCommand controls.
function setUpNavigation() {
    var appBarCommands = document.querySelectorAll('#navBar .win-command');
    for (var i = 0; i < appBarCommands.length; i++) {
        appBarCommands[i].addEventListener('click', appBarClick);
    }
}

// Handle a button-click event so that the app navigates to a new page.
function appBarClick(evt) {
    var clickedButton = evt.target;
    var location = clickedButton.value;
    nav.navigate(location);
}

Some files you can copy over to your Windows Phone Store app using JavaScript project, but you need to refactor small sections of code that are specific to the platform. For example, if your app has a custom control that integrates a WinJS control, you need to check to see whether that control is supported on Windows Phone. As another example, you might have a Web Worker in your app that imports the WinJS library using importScripts. You need to update the location of the WinJS in the JavaScript file for the Web Worker.

For more information about supported controls, see WinJS API changes for Windows Phone 8.1 and Controls list.

In the Building efficient Windows Store apps with JavaScript code sample, the JavaScript files LOC-worker.js (/js/LOC-worker.js) and SearchLOCControl.js (/js/SearchLOCControl.js) can be reused in the Windows Phone project, but are slightly revised. The LOC-worker.js file only needs a single change: the location of the WinJS library needed to be updated.

Updating the location of WinJS in a Web Worker

(function () {
    "use strict";

    self.addEventListener("message", function (message) {

        // Updated with the location of the WinJS library on Windows Phone.
        importScripts("//Microsoft.Phone.WinJS.2.1/js/base.js", "searchLoC.js");

        LOCPictures.getCollection(message.data).
            then(
                function (response) {
                    postMessage(response);
                });
    });
})();

The SearchLOCControl.Control custom control contains a WinJS.UI.SearchBox control, which is not supported on Windows Phone. To update the custom control to work on Windows Phone, the SearchBox control needed to be replaced with a INPUT text box and the events handlers need to be adjusted slightly.

Replace a WinJS control

(function () {
    "use strict";

    WinJS.Namespace.define("SearchLOCControl", {
        Control: WinJS.Class.define(function (element) {
                                                // Other initializing code.

            var htmlString = "<h3>Library of Congress Picture Search</h3>" +

               // Replaced WinJS.UI.SearchBox DIV element with INPUT element in string.
               "<input type='search' id='searchQuery' placeholder='Browse pictures' />" +

                  "<br/><br/>" +
                  "<div id='searchResults' class='searchList'></div>" +
                  "<div id='searchResultsTemplate' data-win-control='WinJS.Binding.Template'>" +
                     "<div class='searchResultsItem'>" +
                        "<img src='#' data-win-bind='src: pictureThumb' />" +
                        "<div class='details'>" +
                           "<p data-win-bind='textContent: title'></p>" +
                           "<p data-win-bind='textContent: date'></p>" +
                        "</div></div></div>";
            var that = this;

            // NOTE: This is an unusual technique for accomplishing this
            // task. The code here is written specifically to be terse.
            MSApp.execUnsafeLocalFunction(function () {
                $(that.element).append(htmlString);
                WinJS.UI.processAll().done(function () {
                    that.searchQuery = $("#searchQuery")[0];

                                                                                // Replaced handler for querysubmitted event to change event.
                    that.searchQuery.onchange = that.submitQuery;
                });
            });


        }, {
            submitQuery: function (evt) {

                // Changed event handler to get value of INPUT element.
                var queryString = evt.target.value;
                                                                
                                                                // Other query submission code.
            },
           
            // Other SearchLOCControl.Control members.

        })
    })
})();

Step 5: Revise app pages, content, layout, or style assets

The Page Controls in your app provide the basic form and layout of the pages in your Windows Runtime app, so it's likely that they need to be revised for Windows Phone. (The term Page Control here refers specifically to the HTML, CSS, and JavaScript files that together comprise a single "page" in a Windows Runtime app. These controls are usually added together using the Add New Item dialog box. The individual files usually have a similar naming convention.) With Page Controls, you need to consider not just what features and controls are supported on Windows Phone, but also the form factor and layout that they've been designed for. For Page Controls that contain few WinJS controls or have dynamic styling, you can probably copy the files over directly to your Windows Phone project. Other Page Controls need to be completely rewritten. The key is determining how to address each page.

For each Page Control in your app, consider the following questions:

  • First off, does a Windows Phone version of your app need such a page? You may find that certain parts of your app don't translate well to a mobile platform.
  • How many WinJS controls does the page use? Does it use any controls that are unsupported on Windows Phone?
  • How much does a page rely on a specific form factor or layout? Is the page intended to be displayed in landscape or portrait orientation? Does it have CSS styles that specify exact height, width, spacing, margin, alignment, or any other formatting of elements on the screen?

If you decide to copy a Page Control to your Windows Phone project "as-is", keep in mind that you must ensure that the HTML page references the correct version of the WinJS. Also, the Page Control project item in Visual Studio includes a WinJS.UI.BackButton control in the template's HTML. The BackButton control is not supported on Windows Phone, which has a back button built into the device. With any Page Control that you copy to a Windows Phone project, you need to remove any BackButton controls. In addition, the default CSS file for a Page Control sets the left margin for the page at 120 pixels. For some mobile devices, this margin size might be too large.

In the Building efficient Windows Store apps with JavaScript code sample, several of the Page Controls can be copied over to the Windows Phone project with minimal changes. The "Home", "Handling Errors", and "Chained asynchronous" pages can be added to the Windows Phone version of the app with very few changes. After removing the BackButton controls, shortening the margin-left style in the CSS, and updating the references to the WinJS, the pages are ready. The JavaScript "code-behind" files need no changes whatsoever because all of the WinJS APIs used in the files are supported on Windows Phone.

Revised HTML file for a Page Control ported to Windows Phone

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>handlingErrors</title>

    <!-- WinJS references updated for Windows Phone -->
    <link href="//Microsoft.Phone.WinJS.2.1/css/ui-dark.css" rel="stylesheet" />
    <script src="//Microsoft.Phone.WinJS.2.1/js/base.js"></script>
    <script src="//Microsoft.Phone.WinJS.2.1/js/ui.js"></script>

    <link href="handlingErrors.css" rel="stylesheet" />
    <script src="handlingErrors.js"></script>
</head>
<body>
    <div class="handlingErrors fragment">
        <header aria-label="Header content" role="banner">
            <h1 class="titlearea win-type-ellipsis">

                <!-- Removed the BackButton control declared here. -->
                <span class="pagetitle">Handling errors</span>

            </h1>
        </header>
        <section aria-label="Main content" role="main">
            <!-- When clicked, this button raises a custom error. -->
            <button id="throwError">Throw an error!</button>

            <!-- ERROR: AppBarCommands must be button elements
                must be contained within AppBar controls. -->
            <!--<div data-win-control="WinJS.UI.AppBarCommand"></div>-->
        </section>
    </div>
</body>
</html>

Revised CSS file for a Page Control ported to Windows Phone

.handlingErrors section[role=main] {

    /* Reduced the margin-left style by 100 pixels for the main content.
    margin-left: 20px;
    margin-right: 120px;
}

However, some of the Page Controls in the Building efficient Windows Store apps with JavaScript code sample need moderate to considerable revisions for a Windows Phone Store app using JavaScript. For example, the "Scheduler API" Page Control uses a WinJS.UI.Hub control, which is not supported on Windows Phone. The Page Control needs to be revised so that it uses a WinJS.UI.Pivot control instead. The members of the Pivot control are almost exactly the same as the Hub control, with the exception that the Pivot.items member returns an array of WinJS.UI.PivotItem controls and the Hub.sections property returns an array of WinJS.UI.HubSection controls.

HTML Page Control with Pivot control instead of Hub

<div id="featuredHub" data-win-control="WinJS.UI.Pivot">
    <div data-win-control="WinJS.UI.PivotItem"
        data-win-options="{
            header: 'Featured Collection 1'
        }"
        class="section">
    </div>
    <div data-win-control="WinJS.UI.PivotItem"
        data-win-options="{
            header: 'Featured Collection 2'
        }"
        class="section">
    </div>
    <div data-win-control="WinJS.UI.PivotItem"
        data-win-options="{
            header: 'Featured Collection 3'
        }"
        class="section">
    </div>
    <div data-win-control="WinJS.UI.PivotItem"
        data-win-options="{
            header: 'Featured Collection 4'
        }"
        class="section">
    </div>
    <div data-win-control="WinJS.UI.PivotItem"
        data-win-options="{
            header: 'Featured Collection 5'
        }"
        class="section">
    </div>
    <div data-win-control="WinJS.UI.PivotItem"
        data-win-options="{
            header: 'Featured Collection 6'
        }"
        class="section">
    </div>
</div>

Again, keeping form factor and screen size in mind is important when porting a Windows Runtime app to Windows Phone. With the Scheduler API page in the Building efficient Windows Store apps with JavaScript code sample, the Windows version of the app displayed six items for each hub section. The Windows Phone, with a smaller screen size, can't display six items in each PivotItem control easily, requiring that the number of items displayed be reduced to two.

(function () {
    "use strict";

    var dataRequest, jobOwnerToken;
    var scheduler = WinJS.Utilities.Scheduler;

    WinJS.UI.Pages.define("/pages/scheduler/scheduler.html", {
        ready: function (element, options) {
            performance.mark("navigated to scheduler");

            dataRequest = Data.featuredCollections.
                then(function (collections) {
                    performance.mark("got collection");

                    var hub = element.querySelector("#featuredHub");
                    if (!hub) { return; }

                    // The hubSections variable is assigned to by calling
                    // Pivot.items rather than Hub.sections.
                    var hubSections = hub.winControl.items;
                    var hubSection;
                    var collection;
                    var priority;

                    jobOwnerToken = scheduler.createOwnerToken();

                    for (var i = 0; i < hubSections.length; i++) {
                        hubSection = hubSections.getItem(i);
                        collection = collections.getItem(i);
 
                        priority == (i < 2) ? scheduler.Priority.normal : scheduler.Priority.idle;

                        scheduler.schedule(function () {
                            populateSection(this.section, this.collection)
                            },
                            priority,
                            { section: hubSection, collection: collection },
                            "adding hub section").
                        owner = jobOwnerToken;
                    }
                });
            },

            unload: function () {
                dataRequest && dataRequest.cancel();
                jobOwnerToken && jobOwnerToken.cancelAll();
            }

            // Other PageControl members …
        });

    function populateSection(section, collection) {
        performance.mark("creating a hub section");
        section.data.header = collection.data.title;

        var contentElement = section.data.contentElement;
        contentElement.innerHTML = "";

        var pictures = collection.data.pictures;

        // Any more than two pictures per PivotItem doesn't display very well
        // on Phone, so each PivotItem only displays two.
        for (var i = 0; i < 2; i++) {

            $(contentElement).append("<img src='" + pictures[i].pictureThumb + "' />");
            (i % 2) && $(contentElement).append("<br/>")
        }
    }
})();

Remarks

As you saw in this article, the process for porting a Windows Runtime app to Windows Phone can be a relatively straightforward process. A lot of code written for the Windows version of the Windows Runtime app can be reused on Windows Phone without too many changes. The key here is to understand what features are supported on each platform and how your need to adjust the layout and design of your Windows Runtime app for the Windows Phone form factor.

WinJS API changes for Windows Phone 8.1

Controls list

Controls by function

JavaScript project templates for Store apps

How to customize Visual Studio template data

Build apps that target Windows and Windows Phone (Windows Runtime apps using C#/VB and XAML)