I have an expanded version of this post which I might also serialize here. Stay tuned.
It's that part about "a different page" that can bite you in a Windows Store app. Simply said, if the user navigates to a page in your app that creates a Bing Maps control, then navigates away such that the page (and the control) is destroyed, that counts as a transaction. If the user navigates back to that same page again, the new control will count as another transaction.
So clearly, if you have an app with a master-detail arrangement, like a master list of restrauants or hotels from which you navigate into a details page for one location and that page includes a map, a single user could incur many transactions within a single app session.
To avoid this, you'll want to create a single Bing Maps control for the app and keep it around for the app's lifetime (you'd create multiple maps if you need multiple simultaneous maps). As the user navigates, place that control in the page (e.g. parent it into the DOM in an HTML/JS app), and be sure to de-parent the control when the user navigates away so it won't be destroyed. This way the app will only have a single Bing Maps session the whole time, and panning, zooming, and changing map styles is all part of the same session and won't incur other transactions.
There are other transactions as documented on the page linked earlier. Other Bing Maps calls (REST Locations API geocoding, REST Routes API driving directions, etc.) are non-billable after the map session starts as you need to call those services using a Bing Maps session key. Using a session key to call the other API’s will cause those subsequnt transactions to be logged as non-billable since they are part of the map session (i.e.: the calls are invoked during the map session). But do be mindful of additional billable transactions when you use advanced features.
Here is a Bing Maps article that talks about this: http://www.bing.com/blogs/site_blogs/b/maps/archive/2012/02/23/optimizing-bing-maps-platform-sessions-and-transactions-usage.aspx
Here also is documentation on the getCredentials method which you use to get the session key to use in the subsequent REST Locations API geocoding calls, REST Routes API driving directions calls, etc. to ensure they are part of the map session: http://msdn.microsoft.com/en-us/library/gg427609.aspx. (The session key is not the same as the credentials you use to create the control.)
Wrapping up this series from part 3 we have two members of the WinJS.UI namespace that match others in WinJS.Utilites for obscurity.
The first is WinJS.UI.scopedSelect, to which you provide a CSS selector and an element. This function is documented as “Walks the DOM tree from the given element to the root of the document. Whenever a selector scope is encountered, this method performs a lookup within that scope for the specified selector string. The first matching element is returned.” What’s referred to here as a “selector scope” is a property called msParentSelectorScope, which WinJS sets on child elements of a fragment, page control, or binding template. In this way, you can do a querySelector within the scope of a page control, fragment, or template without having to start at the document level. The fact that it keeps going up toward the document root means that it will work with nested page controls or templates.
The other is WinJS.UI.getItemsFromRanges, which takes a WinJS.Binding.List and an array of ISelectionRange objects (with firstIndex and lastIndex properties). It then returns a promise whose results are an array of items in that data source for those ranges. Simply said, this exists to translate multiple selections in something like a ListView control into a flat array of selected items–and, in fact, is what’s used to implement the getItems method of a ListView’s selection property. So if you implement a list control of your own around a WinJS.Binding.List, you can use getItemsFromRanges to do the same. The method is provided, in other words, to work with the data source as that it a separate concern from the ListView itself.
Continuing from part 2 of this series, we’ll finish off the last few obscure methods of WinJS.Utilities. First, the empty method removes all child nodes from a specified element. This is basically a simple iteration over the element’s childNodes property, calling removeNode for each in turn (actually in reverse order). A simple bit of code, but one that you don’t need to write yourself.
Next is eventWithinElement. To this you provide an element and an eventArgs object as you received from some event. The method then checks to see if the eventArgs.relatedTarget element is contained within the element you provide. This basically says that an event occurred somewhere within that element, even if it’s not directly on that element. This is clearly useful for working with events on controls that contain some number of child elements.
Finally there’s getMember, to which you pass a string name of a “member” and a root object (defaults to global). The documentation says that this “Gets the leaf-level type or namespace specified by the name parameter.” What this means is that if you give it a name like “navigate” it will look within the namespace of the root you give for that member and return it. In the case of passing “WinJS” and “navigate” it will find WinJS.Navigation.navigate.
This is particularly useful with WinJS binding initializers that are given source and target property names as arrays of separate identifiers. For example, if I have a data-win-bind attribute with a property like title.style.color, the initializer will get an array with [“title”, “style”, “color”]. To either get or set that property for real, I need to turn it into the actual reference to the title.style.color property of the source of target object.
If I want to get that value from the source object, and the array of names is called “sourceProp,” I can do this: var value = WinJS.Utilities.getMember(sourceProp.join(“.”), source);
If I need to set a value in a target object with the array of named called targetProp, I need to pop the last name from the array, use getMember (if I have other names left), then reference the property with , as in:
var lastProp = targetProp.pop();
var dest = targetProp.length ? WinJS.Utilities.getMember(targetProp,join(“.”), target) : target;
target[lastProp] = newValue;
Continuing from part 1 of this series, the next few methods in WinJS to look at are WinJS.Utilities.data and WinJS.Utilities.convertToPixels.
WinJS.Utilities.data is documented as “gets a data value associated with the specific element.” The data value is always an object and is attached to the element using a property name of _msDataKey. So WinJS.Utilities.data(<element>) always just gives you back that object, or creates one if one doesn’t yet exist. You can then add properties to that object, or retrieve them. Basically this is a tidy way to attach extra data to an arbitrary element knowing that you won’t interfere with the element otherwise. WinJS uses this internally in various places.
WinJS.Utilities.convertToPixels sounds fancier than it is. It’s just a helper to convert a CSS positioning string for an element to a real number. That is, in CSS you often use values suffixes like “px” and “em” and so on that, of course, aren’t values that are meaningful in any computations. This function converts those values to a meaningful number of pixels. With “px” values, or something without suffixes, it’s easy–just pass it to parseInt which will strip “px” automatically if it’s there. For other CSS values–basically anything that starts with a numerical value, what this function does it just assign the value to the elements left property (saving the prior value so nothing gets altered), then reads back the elements pixelLeft property. In other words, it lets the DOM engine handle the conversion which will produce 0 if the value isn’t convertible.
Along these same lines are the following WinJS.Utilities methods:
- getRelativeLeft: gets the left coordinate of an element relative to a specified parent. Note that the parent doesn’t have to be the immediate parent, but can be any other node in the element’s tree. This function then basically takes the offsetLeft property of the element and keeps subtracting off the offsetLeft of the next element up until the designated ancestor is reached.
- getRelativeTop: does the same thing as getRelativeLeft except with offsetTop.
- getContentWidth: returns the offsetWIdth of an element minus the values of borderLeftWidth, borderRightWidth, paddingLeft, and paddingRight, which results in the actual width of the area where content is shown.
- getTotalWidth: returns the offsetWIdth of the element plus the marginLeft and marginRight values.
- getContentHeight: returns the offsetHeight of an element minus the values of borderTopWidth, borderBottomWidth, paddingTop, and paddingBottom, which results in the actual height of the area where content is shown.
- getTotalHeight: returns the offsetHeight of the element plus the marginTop and marginBottom values.
- getPosition: returns an object with properties of left, top, width, and height properties of an element relative to the topmost element in the tree (up to document or body), taking scroll postions into account.
Again, it’s helpful to take a look in base.js for the implementation of these functions so you can see what they’re doing, and appreciate the work they’ll save you!
In this and the next few posts I want to bring up a few of the more obscure features of APIs of WinJS and give a little more explanation about them, or at least just make you aware that they exist!
The first ones in these posts are simple wrappers around a few common DOM operations.
WinJS.Utilities.QueryCollection is a class that wraps an element.querySelectorAll or document.querySelectorAll with a number of useful methods. An instance of this class is created by calling WinJS.Utilities.query(<query>[, ]) where <query> is a usual DOM query string and the optional <element> scopes the query. That is, if you provide <element>, the instance wraps element.querySelectorAll(<query>); if you omit <element>, the instance uses document.querySelectorAll(<query>). Similarly, WinJS.Utilities.id(<id>) does a document.getElementById(<id>), then passes the result to new WinJS.Utilities.QueryCollection. WinJS.Utilities.children creates a QueryCollection that contains children of a specified element.
Anyway, once you have a QueryCollection instance, it provides methods to work with its collection, that is, with the results of the DOM query that in and of itself is just an array. As such, you’d normally be writing plenty of loops and iterators to work with the items in the array, and that’s exactly what QueryCollection provides as a convenience. It follows along with other parts of WinJS, which are utilities that most developers end up writing anyway, so it might as well be in a library!
We can see this in what the individual methods do:
- forEach: calls Array.prototype.forEach.apply on the collection using the given callback function and this argument.
- get: returns  from the array
- setAttribute: iterates the collection and calls setAttribute for each item.
- getAttribute: gets an attribute for the first item in the collection.
- addClass, hasClass, removeClass, toggleClass: iterates the collection and calls WinJS.Utilities.addClass, hasClass, removeClass, or toggleClass for each item, respectively.
- listen, removeEventListener: iterates the collection calling addEventListener or removeEventListener for each item
- setStyle, clearStyle: iterates the collection setting a given style to a value or “”, respectively.
- include: adds other items to this collection. The items can be in an array, a document fragment (DOM node), or a single item.
- query: executes a querySelectorAll on each item in the collection and calls include for each set of results. This could be used, for instance, to extract specific children of the items in the collection and add them to the collection.
- control: given the name of a control constructor (a function) and an options object (as WinJS controls typically use), creates an instance of that control and attaches it to each item in the collection. It’s allowable to call this method with just an options object as the first argument, in which case the method calls WinJS.UI.process on each item in the collection followed by WinJS.UI.setOptions for each control therein. This allows the collection to basically contain elements that have WinJS control declarations (data-win-control attributes) that have not yet been instantiated.
- template: renders a template element that is bound to the given data and parented to the elements included in the collection. If the collection contains multiple elements, the template is rendered multiple times, once at each element per item of data passed.
As for WinJS.Utilities.addClass, WinJS.Utilities.removeClass, WinJS.Utilities.hasClass, and WinJS.Utilities.toggleClass, as used above, these are helper functions that simply add one or more classes (space delimited) to an element, removes a single class from an element, checks if an element has a class, and adds or removes a class from an element depending on whether it’s already applied. While these operations sound simple, the implementation is actually quite non-trivial. Take a look at the WinJS source code (in base.js) to see what I mean! Good code that you don’t have to write.
When testing your app, keep a keen eye out for how and when progress indicators are showing up, or how they aren’t showing up for that matter. Generally speaking progress indicators show that some long-running process is happening or that the app is waiting for some data or other results to be delievered. The latter is probably the most common, because long running processes like image manipulation are under the app’s control and is just a matter of crunching the pixels. Network latency, on the other hand, whether from connection timeouts of slow transfer speeds, isn’t something the app can do much about other than wait.
To simulate a slow connection, you can, of course, create additional traffic on your connection while you’re testing the app–e.g. playing videos, running big downloads (and uploads), and so forth. The faster your connection, the more you’ll have to load it up. You might also be able to change settings in your modem or router to make everything run more slowly. For example, if you have a wireless-N router, change it to run only at wireless-A speeds, then load it up with extra work.
Whatever the case, slowing down the network speed will help show where and how progress indicators are being used. You’ll want to especially look for places where your app just appears to sit without any visual indication of the work it’s doing. This reveals that your code is making assumptions about how quickly data is coming back and that you’ll need to put up a progress indicator if the user waits for more than 1-2 seconds (the recommended timeout for showing an indicator).
On a slow connection, too, you can better evaluate whether you’re overusing progress indicators. That is, while it’s nice that we have controls to show work happening, they are something that users will get tired of looking at over and over. So think about how you might architect the app to wholly eliminate the need for those indicators. For example, if you are switching between a page with a list of items and a details page for one item, and there’s a delay in that switch, it’s a good place to simply switch the visibility of two pages that are fully constructed, rather than doing a real navigation that would tear down and rebuild the pages each time. The user won’t be able to tell the difference, except that everything is running much faster. Indeed, building a page with a list control of hundreds or thousands of items is a very expensive process, so avoiding that work is a good thing, especially when the list page itself doesn’t change in the process of navigating in and out of details.
On the flip side, also test your app on the fastest connection you can find and see if there are any places where progress indicators show up unnecessarily. This would also reveal places where you’re assuming that the data will take a long time to obtain, and where a progress indicator might just flash on the screen momentarily and create visual noise. In those cases you’ll want to make sure you again use a timeout before the indicator appears.