July 2013

Volume 28 Number 7

Modern Apps - Mastering Controls and Settings in Windows Store Apps Built with JavaScript

By Rachel Appel | July 2013

Rachel AppelGreat user experiences present data to users in natural and intuitive ways regardless of form factor. Presenting data and content requires updated APIs, controls and tools for building modern experiences. In Windows Store apps, the amount of code required and the complexity of controls depends on what kind of app you create, whether it’s a productivity app, game, social app or financial app. Windows Library for JavaScript (WinJS) controls are easy to master for any developer building Windows Store apps with JavaScript, and they’re what I’ll discuss here.

Windows 8 Brings a New UI Paradigm and New UI Controls

Windows Store apps look and behave much differently than programs running in previous versions of Windows. There have 1been some major renovations in Windows, starting with the new Start page full of live tiles as your first interaction with apps. Other obvious changes are that Windows Store apps run in full-screen mode or snapped view, putting content at the front and center while commands and menus stay out of sight until the user requests them.

Windows UI elements such as the minimize, maximize, and close buttons that were once ubiquitous no longer exist in Windows Store apps, as touch swipes and mouse movements have rendered them useless. To close an app you need only swipe or mouse down from the top of the screen to the bottom. Even menus are no longer a fundamental fixture at the top of every screen. In Windows Store apps menus remain tucked away until a touch swipe or mouse gesture reveals them from the bottom in an AppBar as shown in Figure 1, using a small countdown timer app as an example.

The AppBar at the Bottom of the App
Figure 1 The AppBar at the Bottom of the App

As you can see in Figure 1, menus are out, and commanding elements are graphics-first with some text rather than traditional text-first menus with occasional graphics. These elements are also perfectly finger-sized. If you need more room for options than just the bottom, you can place a nav bar, which is simply an AppBar at the top of the page.

Navigating in traditional Windows menus can be downright painful at times. We’ve all cringed at a program with a cascading menu that takes you 13 levels deep, causing you to forget what you were looking for in the first place. In Windows Store apps, navigation weaves itself into the content, because touch and mouse gestures on ListView items invoke other pages. Pinch gestures and control key+mouse wheel activate zooming with semantic zoom (bit.ly/16IDtdi), which is both a control and navigation paradigm in Windows Store apps. Semantic­Zoom is part of the complete listing of WinJS controls (bit.ly/w1jLM5).

Working with HTML and WinJS Controls

There are two primary kinds of controls in Windows Store apps using JavaScript: standard HTML elements and WinJS controls. WinJS controls are HTML combined with prebuilt JavaScript that extends how HTML elements look or behave. Because they’re HTML, you can style WinJS controls with CSS. Figure 2 is an example of a basic WinJS control, the WinJS DatePicker, which is a control comprising multiple DropDown controls representing the day, month and year, showing the default output from this code:

<span id="eventDate" data-win-control="WinJS.UI.DatePicker" />

The WinJS DatePicker Control
Figure 2 The WinJS DatePicker Control

Of course, the DatePicker control in Figure 2 has no styling outside of the default WinJS styles, but you can change that by overriding the .win-datepicker-date, .win-datepicker-month and .win-datepicker-year WinJS CSS selectors as well. Use .win-datepicker to style the entire control.

The reason the DatePicker (or any WinJS control) works the way it does is due to the HTML5 data-* attributes, namely data-win-control. The data-win-control attribute signifies the type of control that WinJS will render, in place, so when you set the value of the data-­win-control attribute to WinJS.UI.DatePicker, the control renders the dropdowns in Figure 2. The data-win-options attribute allows you to set additional properties on controls. For example, on the DatePicker you can set data-win-options for the default display date and minimum and maximum date ranges. Although it’s named DatePicker, you can change the control to capture the time instead—for example, hours, minutes and seconds.

Because WinJS builds and renders the final control output, the design-time HTML and the HTML at run time look quite different. Figure 3 demonstrates the HTML that WinJS injects into the host element at run time. This is viewable from the DOM Explorer (Debug | Windows | DOM Explorer).

Figure 3 The DatePicker Renders Three DropDowns Filled with Options for Date/Month/Year

<span class="win-datepicker" id="eventDate" role="group"
   lang="en-US" dir="ltr" data-win-control="WinJS.UI.DatePicker">
<select tabindex="0" class="win-datepicker-month win-order0"
   aria-label="Select Month">
<option value="January">January</option>
<option value="February">February</option>
<option value="March">March</option>
<option value="April">April</option>
<!-- more <options> that show the other months -->
</select>
<select tabindex="0" class="win-datepicker-date win-order1"
   aria-label="Select Day">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<!-- more <options> that  show day numbers -->
</select>
<select tabindex="0" class="win-datepicker-year win-order2"
   aria-label="Select Year">
<option value="1913">1913</option>
<option value="1914">1914</option>
<option value="1915">1915</option>
<option value="1916">1916</option>
<!—more <options> that show years -->
<option value="2112">2112</option>
<option value="2113">2113</option>
</select>

The code backing WinJS controls such as the DatePicker lives in a file located at <ProjectRoot>\References\Windows Library for JavaScript 1.0\js\ui.js, alongside some core WinJS friends. Notice that this is the same <script> reference as the one required in the <head> element of Windows Store app pages, that is:

<script src="//Microsoft.WinJS.1.0/js/ui.js"></script>

Modify these files at your own peril, as they’re composed of core WinJS code.

Any WinJS control, including the DatePicker, is accessible at run time through a property named winControl. WinJS appends child properties to the winControl property at run time that are specific to the type of WinJS control. As an example, a ListView contains its list of items, or you can query a WinJS.UI.Ratings control for the user-selected rating. You can access the element’s winControl property like so:

var control = document.getElementById("WinJSElementId").winControl

Buttons,CheckBoxes, RadioButtons, DropDowns, TextBoxes and the like all work exactly the way they do in any plain old HTML page; however, the WinJS.UI namespace is full of UI controls for many complex scenarios, including the ever-important list controls.

List and Grid Controls

Many types of apps need to present data in a grid or in a list, so of course there’s a control for those scenarios called the ListView, which can render itself as a grid or a list, complete with grouping and variable item sizes. The ListView is not only highly flexible, but it works perfectly in the new Windows experience by doing things such as automatically scaling to fit the screen and placing list items in varying-sized rows and columns depending on resolution and device-display size.

While most other WinJS controls stand alone, ListViews work in tandem with corresponding HTML as a template. This means you need to set up both template HTML and the control container itself, as shown in Figure 4. Notice the template’s data-win-control and the ListView’s data-win-options attributes contain settings that link the ListView and its template together.

Figure 4 The HTML Required to Create a WinJS ListView

<div id="maincontent">     
  <div id="listViewTemplate"
         data-win-control="WinJS.Binding.Template" >
    <div data-win-bind="style.background: color" class="win-item">
      <h1 data-win-bind=" innerText: daysToGo"></h1>
      <h2 class="subtitle" 
             data-win-bind="innerText: eventTitle"></h2><br />
      <h2 class="subtitle-bottom" 
             data-win-bind=" innerText: eventDate"></h2>
    </div>
  </div>
  <div id="listView" data-win-control="WinJS.UI.ListView"
    class="win-listview"
    data-win-options=
    "{ itemDataSource: Data.items.dataSource,
    itemTemplate: select('#listViewTemplate'),
    selectionMode: 'single'}">
  </div>
</div>

Figure 4 contains two <div> elements, one for the template with an id of listViewTemplate and the ListView itself, named listView. The listViewTemplate element contains child elements that represent different fields for each item in the list or grid, such as the eventTitle or eventDate. Looking at the ListView in Figure 4 reveals the itemDataSource property set to Data.items.dataSource, meaning that Data is a namespace and items is a WinJS.Binding.List object populated with data. Because JavaScript works with loosely typed data, all you need to do is stuff an array of objects into the List constructor and it’s then ready to bind to ListView controls, similar to this code:

WinJS.Namespace.define("Data", {
    items: []
});

// NOTE: The 'month' parameter for the Date constructor expects
// the month as a 0-based index (Jan = 0; Dec = 11).
var list = [
  { eventTitle: "Rachel's Birthday", 
   eventDate: new Date(2014, 0, 13) },
  { eventTitle: "Rachel's BFF's Birthday", 
    eventDate: new Date(2013, 4, 29) }
];

list.map(function (item) {
    var daysToGo = item.eventDate.getTime() - Date.now();
    item["daysToGo"] = (daysToGo / (1000 * 60 * 60 * 24)).toFixed();
});

Data.items = new WinJS.Binding.List(list);

Alternatively, you can use the push method to push items onto the List object rather than passing an array to the List’s constructor. The best way to manage data in a ListView is by exposing relevant options (add, delete and so on) via an AppBar control.

AppBars and Commands

Content over chrome is an important Microsoft design principle. AppBars are an integral part of this design principle, as they stay out of sight, waiting to present their options when you need them. In code, an AppBar is just a <div> that contains one or more <button> elements, named “app bar commands,” with their data-win-control attributes set to WinJS.UI.AppBarCommand. The distinguishing feature between the individual AppBar commands lies in the data-win-options, as you might have guessed.

Inspecting the data-win-options in Figure 5 for each AppBar command reveals the id, label, icon and section for each command. You can assign AppBar buttons to the global section of the AppBar (which displays on the bottom right of the app’s screen), or set the section option to “selection” (to display on the bottom left). Setting the section option of AppBar commands to “selection” makes them contextual, for use when a user selects an item in the ListView by swiping or clicking.

Figure 5 Building an AppBar

<!-- HTML -->
<div id="appbar" class="win-appbar" 
       data-win-control="WinJS.UI.AppBar">
<button data-win-control="WinJS.UI.AppBarCommand"
   data-win-options="{id:'deleteButton', 
    label:'Delete',
    icon:'delete', section:'selection'}" 
    type="button"></button>
<button data-win-control="WinJS.UI.AppBarCommand"
   data-win-options="{id:'addButton', 
    label:'Add', icon:'add',
    section:'global'}" 
    type="button"></button>
<button 
   data-win-control="WinJS.UI.AppBarCommand"
   data-win-options="{id:'refreshButton',
   label:'Refresh',
   icon:'refresh', 
   section:'global'}" 
   type="button"></button>
</div>
// JavaScript
document.getElementById("addButton").addEventListener(
  "click", this.addButtonClick);
document.getElementById("deleteButton").addEventListener(
  "click", this.deleteButtonClick);
document.getElementById("refreshButton").addEventListener(
  "click", this.refreshButtonClick);

In the HTML page’s related JavaScript file, attach event listeners to the AppBar buttons just as you would any other HTML element. You need no listeners for the AppBar itself to appear, as it shows and hides itself automatically in response to user commands—­although you also can invoke it programmatically. The sample in Figure 5 shows a complete AppBar with buttons to add, delete and refresh data.

You can write code to show, hide, enable and disable AppBar buttons, as the scenario demands. 

Flyouts

Because a touchscreen is a first-class citizen, you may have noticed that when UI elements and dialog boxes show themselves, you can easily send them away simply by tapping or clicking on any part of the screen other than the dialog itself. This notion of implicitly closing the dialog is called a “light dismiss,” and it’s the default behavior of MessageDialogs and PopupMenus in Windows 8, because a light dismiss is far easier on the user than dealing with close buttons.

Just as with the previous controls, the Flyout uses the data-­win-control attribute to specify that it’s indeed a WinJS.UI.Flyout control. Children of the Flyout <div> element render inside the Flyout. For example, you might put an HTML form in a Flyout so the user can fill in a title and date for an upcoming event, as the code in Figure 6 illustrates, producing what you see in Figure 7.  

Figure 6 A Light-Dismiss Flyout to Collect Information with WinJS Controls

<!-- HTML  -->
<div id="eventFlyoutPanel" data-win-control="WinJS.UI.Flyout">
  <table width="100%" height="100%">    
    <tr><td>Event Title:</td><td><input type="text"
 id="eventTitle"/></td></tr>
    <tr><td>Event Date:</td><td id="eventDate"
       data-win-control="WinJS.UI.DatePicker"></td></tr>        
    <tr><td>&nbsp;</td><td align="right">
    <input type="button" id="confirmButton" value="Submit" /></td></tr>
    </table>
</div>
// JavaScript
addButtonClick: function () {
  document.getElementById("eventFlyoutPanel").winControl.show(
  "addButton", "top");
}

A Flyout to Collect Information
Figure 7 A Flyout to Collect Information

Notice that the Flyout in Figure 7 is just an HTML form. When the user taps or clicks on the Add AppBar command, the Flyout appears, as directed in the addButtonClick function in Figure 6. Flyouts appear on screen in relation to other controls, so when you call the winControl.show method, you pass the control anchor element’s name as well as where to place the control—that is, next to the anchor control’s top or bottom edge.

The user can tap or click anywhere away from the Flyout to make it go away because it’s a light-dismiss control. You’ll notice a distinct lack of modal dialog boxes in Windows Store apps, which is part of the Microsoft design philosophy. The folks in design circles frown upon modal dialog boxes for good reason—anything that annoys a user or blocks him from free movement is considered poor design.

Another type of Flyout is the SettingsFlyout, which is a big shift from the way apps in earlier versions of Windows manage user preferences.

App Settings

Windows users are no strangers to the customary Tools | Options or Help | About menu options that launch dialogs with a labyrinth of settings. Fortunately, what has replaced these dialogs in the land of Windows Store apps is more intuitive for the user. Both settings and about pages operate as tall, vertical Flyout controls, invoked when the user selects the Settings icon from the Windows charms (bit.ly/146cniM).

Settings work consistently across Windows Store apps. When the user invokes the Settings charm, the same SettingsFlyout appears on the right no matter which app is running. Links to your privacy policy, user preferences, help and so on belong in the SettingsFlyout. Creating links to your privacy or options pages takes only a few lines of code in the app.onactivated event normally found in /js/default.js:

// In default.js, app.onactivated

WinJS.Application.onsettings = function (e) {

  e.detail.applicationcommands =
   { "privacypolicy": { title: "Privacy Policy",
 href: "privacy.html" } };

    WinJS.UI.SettingsFlyout.populateSettings(e);

};

Once the user taps or clicks on one of the Settings links, its corresponding Flyout appears. Figure 8 contains the HTML for a SettingsFlyout containing privacy policy information (a clear and concise privacy policy is a requirement for publishing in the Windows Store).

Figure 8 The HTML for a Settings Flyout Containing Privacy Policy Information

<div id="settingsFlyout" 
  data-win-control="WinJS.UI.SettingsFlyout"
  data-win-options="{settingsCommandId:'privacypolicy', width:'narrow'}">
  <div class="win-header" style="background-color:#312e2e">
    <button type="button" onclick="WinJS.UI.SettingsFlyout.show()"
       class="win-backbutton"></button>
    <div class="win-label">Privacy Policy</div>
  </div>
  <div class="win-content">
    <div class="win-settings-section">
      <p>This application does not collect any personal information.</p>
      <p>Internet access is only used to retrieve data from the web,<div id="settingsFlyout" 
  data-win-control="WinJS.UI.SettingsFlyout"
  data-win-options="{settingsCommandId:'privacypolicy', width:'narrow'}">
  <div class="win-header" style="background-color:#312e2e">
    <button type="button" onclick="WinJS.UI.SettingsFlyout.show()"
       class="win-backbutton"></button>
    <div class="win-label">Privacy Policy</div>
  </div>
  <div class="win-content">
    <div class="win-settings-section">
      <p>This application does not collect any personal information.</p>
      <p>Internet access is only used to retrieve data from the web,
         or to allow you to contact the developer:</p>
          <p>
            <a href="mailto:rachel@rachelappel.com">Email Rachel Appel </a>
 <br />
            <a href="https://rachelappel.com/privacy-policy"
             target="_blank">View privacy statement online</a>
          </p>
      </div>
  </div>
</div>

         or to allow you to contact the developer:</p>
          <p>
            <a href="mailto:rachel@rachelappel.com">Email Rachel Appel </a>
 <br />
            <a href="https://rachelappel.com/privacy-policy"
             target="_blank">View privacy statement online</a>
          </p>
      </div>
  </div>
</div>

Don’t forget to name the privacy policy settings file the same as the href argument used to register the Flyout (see Figure 8).

Privacy policies aren’t the only things you can put in Settings. SettingsFlyouts can contain any valid HTML and often are host to ToggleSwitches, CheckBoxes, and DropDowns, and function just as the Tools | Options dialogs do now. However, as mentioned, SettingsFlyouts are light-dismiss controls, so a simple tap elsewhere makes them vanish, quite the opposite of modal dialogs. Another simple yet new paradigm in Windows Store app development is the SemanticZoom control, a handy navigation helper.

Semantic Zoom

Some apps are data-intensive. Navigating those apps can be quite difficult, especially when they handle a lot of data. This is where semantic zoom comes to the rescue. Semantic zoom lets you express two modes of data visualization: zoomed in and zoomed out. The zoomed-in mode, the default, shows all the data possible, and the user must pan or scroll through it. The zoomed-out mode usually forms an aggregate representation of data, making it easy for the user to navigate to an area of data and then zoom in to a specific data item.

A SemanticZoom is a set of three controls: the host control and the two zoomed controls, as shown in Figure 9. The child controls must implement IZoomable to participate in semantic zoom, so for WinJS apps, the ListView is the only one that will work.

Figure 9 Code for the SemanticZoom Control

<div id="semanticZoomDiv" data-win-control="WinJS.UI.SemanticZoom">            
  <!-- The zoomed-in view. -->   
  <div id="zoomedInListView"
    data-win-control="WinJS.UI.ListView"
    data-win-options="{ itemDataSource:
      myData.groupedItemsList.dataSource,
     itemTemplate: select('#mediumListIconTextTemplate'),
     groupHeaderTemplate: select('#headerTemplate'),
     groupDataSource: myData.groupedItemsList.groups.dataSource,
     selectionMode: 'none',
     tapBehavior: 'none',
     swipeBehavior: 'none' }">  </div>
  <!-- The zoomed-out view. -->
  <div id="zoomedOutListView"
    data-win-control="WinJS.UI.ListView"
    data-win-options="{ itemDataSource:
     myData.groupedItemsList.groups.dataSource, itemTemplate:
     select('#semanticZoomTemplate'), selectionMode: 'none',
     tapBehavior: 'invoke', swipeBehavior: 'none' }">
  </div>
</div>

As you can see, semantic zoom is just flipping between two ListViews, making it a great alternative mode of transportation for users—and easy to implement for developers.

More Controls

There are more controls—such as the Progress bar, FlipView, pop-up menus, MessageDialog and Ratings—that are all part of the new Windows experience, but I don’t have room here to discuss them all. Open standards such as HTML5 and ECMAScript 5 (ES5) are the base for all things WinJS, so everything from Web staples to anchors and inputs to HTML5 audio and video all work nicely as part of the Windows Store app development platform.


Rachel Appel is a consultant, author, mentor and former Microsoft employee with more than 20 years of experience in the IT industry. She speaks at top industry conferences such as Visual Studio Live!, DevConnections, MIX and more. Her expertise lies within developing solutions that align business and technology focusing on the Microsoft dev stack and open Web. For more about Appel, visit her Web site at rachelappel.com.

 Thanks to the following technical expert for reviewing this article: Keith Boyd (Microsoft)