Data Binding, Templates, and Collections - Programming Windows Store Apps with HTML CSS and JavaSript (2014)

Programming Windows Store Apps with HTML CSS and JavaSript(2014)

Chapter 6
Data Binding, Templates, and Collections

Having just now in Chapter 5, “Controls and Control Styling,” thoroughly teased you with plenty of UI elements to consider, I’m going to step away from UI and controls for a short time to talk about glue. You see, I have a young son and glue is a big part of his life! OK, I’m joking about the real glue, but it’s an appropriate term when we talk about data binding because data binding is, in many ways, a kind of glue that keeps the UI we build with various controls appropriately attached to the data that the UI represents.

A while back I saw a question on one of the MSDN forums that asked, simply, “When do I use data binding at all?” It’s a good question, because writers of both books and documentation often assume that their readers know the why’s and when’s already, so they just launch into discussions about models, views, and controllers, tossing out acronyms like MVC, MVVM, MVMMVMV, MMVMMVMVMVMVMCVVM, and so on until you think they’re revving up an engine for some purpose or another or perhaps getting extra practice at writing confusing Roman numerals! Indeed, the whole subject can often be shrouded in some kind of impenetrable mystique. As I don’t at all count myself among the initiates into such mysteries, I’ll try to express the concepts in prosaic terms. I’ll covers the basics of what data binding is and why you care about it, and then I’ll demonstrate how data binding is expressed through WinJS. (Though you can implement it however you like, WinJS serves as a helpful utility here, as is true for most of the library. Other developers employ angular.js for this purpose, which is certainly another option but one that I don’t address in this book.)

At first we’ll be looking at data binding with simple data sources, such as single objects with interesting properties. Where data binding really starts to prove its worth, however, is with collections of such objects, which is exactly why we’re talking about it here before going on to Chapter 7, “Collection Controls.” To that end, we’ll explore the different kinds of collections that you might encounter when writing apps, including those from WinRT, such as the vector, and the WinJS.Binding.List that is an essential building block for collection controls.

Speaking of building blocks, this chapter also includes a subject that flows naturally from the others: templates. A template is a way to define how to render some data structure such that you can give it any instance of that structure and have it produce UI. Again, this is clearly another building block for collection controls, but it’s something you can use separately from them.

So let’s play with the glue and get our hands sticky!

Data Binding

Put simply, data binding means to create relationships between properties of data objects and properties of UI elements (including styles). This way those UI elements automatically reflect what’s happening in the data to which they are “bound,” which is often exactly what you want to accomplish in your user experience. Data binding can also work in the other direction: data objects can also be bound to UI elements such that changes a user makes in the UI are reflected back to the data objects.

When small bits of UI and data are involved, you can easily set up all these relationships however you want, by directly initializing UI element values from their associated data object and updating those values when the data is modified. You’ve probably already written plenty of code like this. And to go the other direction, you can watch for change events on those UI elements and update the data objects in response. You’ve probably written that kind of code as well!

As the UI gets more involved, however, such as when you're using collections or you're setting up relationships to multiple properties of multiple elements, having more formalized structures to handle the data-to-UI wiring starts to make a lot of sense, especially when you start dealing with collections. It’s especially helpful to have a declarative means to set up those relationships, rather than having to do it all through procedural code. In this section, then, we’ll start off with the basics of how data binding works, and then we’ll look at the features of the WinJS.Bindingnamespace and what it has to offer (including declarative structures). This gives us the basis for working with collections, which leads us naturally (in the next chapter) to how we bind collections to collection controls.

Let me note that we won’t be talking about other formalized patterns and coding conventions for data binding, such as “model-view-controller” (MVC), “model-view-viewmodel” (MVVM), and others, primarily because this book’s focus is on the features of the Windows platform more than higher-level software engineering practices. (Indeed, I’ve often found discussions of data binding to start right in on patterns and frameworks, leaving the basics of data binding in the dust!) If you like working with these patterns, you can of course design your app’s architecture accordingly around the mechanisms that WinJS or other frameworks provide.

Data Binding Basics

The general idea of data binding is again to connect or “bind” properties of two different objects together, typically properties of a data object—or context—and properties of a UI object. We can generically refer these as source and target. A key here is that data binding generally happens between properties of the source and target objects, not the objects as a whole.

The binding can also involve converting values from one type into another, such as converting a set of separate source properties into a single string as suitable for the target. It’s also possible to have multiple targets bound to the same source or one target bound to multiple sources. This flexibility is exactly why the subject of data binding can become somewhat nebulous, and why numerous conventions have evolved around it! Still, for most scenarios, we can keep the story simple.

A common data-binding scenario is shown in Figure 6-1, where we have specific properties of two UI elements, a span and an img, bound to properties of a data object. There are three bindings here: (1) the span.innerText property is bound to the source.name property; (2) the img.srcproperty is bound to the source.photoURL property; and (3) the span.style.color property is bound to the output of a converter function that changes the source.userType property into a color.

images

FIGURE 6-1 A common data-binding scenario between a source data object and two target UI elements, involving two direct bindings and one binding with a conversion function.

How these bindings actually behave at run time then depends on the particular direction of each binding, which can be one of the following (omitting any converters that might be involved):

One-time: the value of the source property is copied to the target property at some point, after which there is no further relationship. This is what you automatically do when passing variables to control constructors, for instance, or simply initializing target property values with source properties. What’s useful here is to have a declarative means to make such assignments directly in element attributes, as we’ll see.

images

One-way: the target object listens for change events on bound source properties so that it can update itself with new values. This is typically used to update a UI element in response to underlying changes in the data. Changes within the target element (like a UI control), however, are not reflected back to the data itself (but can be sent elsewhere as with form submission, which could in turn update the data through another channel).

images

Two-way: essentially one-way binding in both directions, as the source object also listens to change events for target object properties. Changes made within a UI element like a text box are thus saved back in the bound source property, just as changes to the source property update the UI element. Obviously, there must be some means to not get stuck in an infinite loop; typically, both objects avoid firing another change event if the new value is the same as the existing one.

images

Data Binding in WinJS

Now that we’ve seen what data binding is all about, we can see how it can be implemented within a Windows Store app. Again, you can create whatever scheme you want for data binding or use a third-party JavaScript library for the job: it’s just about connecting properties of source objects with properties of target objects.

If you’re anything like a number of my paternal ancestors, who seemed to wholly despise relying on anyone to do anything they could do themselves (like drilling wells, mining coal, and manufacturing engine parts), you may very well be content with engineering your own data-binding solution. But if you have a more tempered nature like I do (thanks to my mother’s side), I’m delighted when someone is thoughtful enough to create a solution for me. Thus my gratitude goes out to the WinJS team who, knowing of the common need for data binding, created theWinJS.Binding API. This supports one-time and one-way binding, both declaratively and procedurally, along with converter functions. At present, WinJS does not provide for two-way binding, but such structures aren’t difficult to set up.

Within the WinJS structures, properties of multiple target elements can be bound to a single data source property. WinJS.Binding, in fact, provides for what are called templates, basically collections of target elements whose properties are all bound to the same data source, as we’ll see later in this chapter. Though we don’t recommend it, it’s possible to bind a single target element to multiple sources, but this gets tricky to manage properly. A better approach in such cases is to wrap those separate sources into a single object and bind to its properties instead. This way the wrapper object can manage the process of combining multiple source properties into a single one to use in the binding relationship.

To understand core data binding with WinJS, let’s look at how we’d write our own binding code and then see the solution that WinJS offers. We’ll use the scenario shown in Figure 6-1, where we have a source object bound to two separate UI elements, with one converter that changes a source property into a color.

One-Time Binding

One-time binding, as mentioned before, is essentially what you do whenever you just assign values to properties of an element. Consider the following HTML, which is found in Test 1 of the BindingTests example in this chapter’s companion content:

<!-- Markup: the UI elements we'll bind to a data object -->

<section id="loginDisplay1">

<p>You are logged in as <span id="loginName1"></span></p>

<img id="photo1"></img>

</section>

Given the following data source object:

var login1 = { name: "liam", id: "12345678",

photoURL: "http://www.kraigbrockschmidt.com/images/Liam07.png", userType: "kid"};

we can bind as follows, where we include a converter function (userTypeToColor) in the process:

//"Binding" is done one property at a time, with converter functions just called directly

var name = document.getElementById("loginName1");

name.innerText = login1.name;

name.style.color = userTypeToColor(login1.userType);

document.getElementById("photo1").src = login1.photoURL;

function userTypeToColor(type) {

return type == "kid" ? "Orange" : "Black";

}

This gives the following result, in which I shamelessly publish a picture of my kid as a baby:

images

With WinJS we can accomplish the same thing by using a declarative syntax and a processing function. In markup, we use the attribute data-win-bind to map target properties of the containing element to properties of the source object. The processing function,WinJS.Binding.processAll, then creates the necessary code to implement those binding relationships. This follows the same idea as using the declarative data-win-control and data-win-options attributes instruct WinJS.UI.process[All] how to instantiate controls, as we saw in Chapter 5.

The value of data-win-bind is a string of property pairs. Each pair’s syntax is <target property> : <source property>[<initializer>] where the <initializer> is an optional function that determines how the binding relationship is set up.

Each property identifier can use dot notation as needed (see the sidebar coming up for additional syntax), and uses JavaScript property names. Property pairs are separated by a semicolon, as shown in the HTML for Test 2 in the example:

<section id="loginDisplay2">

<p>You are logged in as

<span id="loginName2"

data-win-bind="innerText: name; style.color: userType Tests.typeColorInitializer">

</span>

</p>

<img id="photo2" data-win-bind="src: photoURL"/>

</section>

Tip The syntax of data-win-bind is different than data-win-options! Whereas the options string is an object within braces { } with options separated by a comma, a binding string has no braces and items are separated by semicolons. If you find exceptions being thrown fromBinding.processAll, check that you have the right syntax.

Here we’re saying that the innerText property of the loginName2 target is bound to the source’s name property and that the target’s style.color property is bound to the source’s userType property according to whatever relationship the Tests.typeColorInitializer establishes. As we’ll see in a moment, that initializer is built directly around the userTypeToColor converter.

As you can see, the data-win-bind notation above does not specify the source object. That’s the purpose of Binding.processAll. So, assuming we have a data source as before:

var login2 = { name: "liamb", id: "12345678",

photoURL: "http://www.kraigbrockschmidt.com/images/Liam07.png", userType: "kid"};

we call processAll with the target element and the source object as follows:

//processAll scans the element'stree for data-win-bind, using given object as data context

WinJS.Binding.processAll(document.getElementById("loginDisplay2"), login2);

The data context The second argument to Binding.processAll is the data context (or source) with which to perform the binding. If you omit this, it defaults to the global JavaScript context. If, for example, you have a namespace called Data that contains a property bindSource, you can specify Data.bindSource in data-win-bind and omit a data context in processAll, or you can specify just bindSource in data-win-bind and pass Data as the second argument to processAll.

Other arguments Binding.processAll performs a deep traversal of all elements contained within the given root, so you need only call it once on that root for all data binding in that part of the DOM. If you want to process only that element’s children and not the root element itself, passtrue as the third parameter (called skipRoot). A fourth parameter called bindingCache also exists as an optimization for holding the results of parsing data-win-bind expressions. Both skipRoot and bindingCache are useful when working with binding templates that we’ll talk about toward the end of this chapter.

The result of all this, in Test 2, is identical to what we did manually in Test 1. In fact, processAll basically takes the declarative data-win-bind syntax along with the source and target and dynamically executes the same code that we wrote out explicitly in Test 1. The one added bit is that the initializer function must be globally accessible and marked for processing, which is done as follows:

//Use a namespace to export function from the current module so WinJS.Binding can find it

WinJS.Namespace.define("Tests", {

typeColorInitializer: WinJS.Binding.converter(userTypeToColor)

});

As with control constructors defined with WinJS.Class.define, WinJS.Binding.converter automatically marks the functions it returns as safe for processing. It does a few more things as well, but we’ll return to that subject in a bit.

We could also put the data source object and applicable initializers within the same namespace.51 For example, in Test 3 we place our login data object and the typeColorInitializer function in a LoginData namespace, and the markup and code now look like this:

<section id="loginDisplay3">

<p>You are logged in as

<span id="loginName3"

data-win-bind="innerText: name; style.color: userType LoginData.typeColorInitializer">

</span>

</p>

<img id="photo3" data-win-bind="src: photoURL"/>

</section>

WinJS.Binding.processAll(document.getElementById("loginDisplay3"), LoginData.login);

WinJS.Namespace.define("LoginData", {

login : {

name: "liamb", id: "12345678",

photoURL: "http://www.kraigbrockschmidt.com/images/Liam07.png",

userType: "kid"

},

typeColorInitializer: WinJS.Binding.converter(userTypeToColor)

});

In summary, for one-time binding WinJS.Binding simply gives you a declarative syntax to do exactly what you’d do in code, with a lot less code. Because it’s all just some custom markup and a processing function, there’s no magic here, though such useful utilities are magical in their own way! In fact, the code here is really just one-way binding without having the source fire any change events. We’ll see how to do that with WinJS.Binding.as in a moment after a couple more notes.

First, Binding.processAll is actually an async function that returns a promise. Any completed handler given to its then/done method will be called when the processing is finished, if you have additional code that’s depending on that state.

Second, you can call Binding.processAll more than once on the same target element, specifying a different source object (data context) each time. This won’t replace any existing bindings, mind you—it just adds new ones, meaning that you could end up binding the same target property to more than one source, which could become a big mess. So again, a better approach is to combine those sources into a single object and bind to that, using dot notation to identify nested properties.52

Third, the binding that we’ve created with WinJS here is actually one-way binding by default, not one-time, because one-way binding is the most common scenario. To get true one-time binding, use the built-in initializer function, oneTime, in your data-win-bind string. More on this under “Binding Initializers” later on.

Sidebar: Additional Property Syntax and Binding to WinJS Controls

Property identifiers in data-win-bind can be single identifiers like name or compound identifiers such as style.color. This applies to both source and target properties. This is important when binding to properties of a WinJS control, rather than its root element, as those properties must be referenced through winControl. For example, if we had a source object called myData and wanted to bind some of its properties to those of a WinJS.UI.Rating control, we’d use the following syntax:

<div id="rating1" data-win-control="WinJS.UI.Rating"

data-win-options="{onchange: changeRating}"

data-win-bind="{winControl.averageRating: myData.average,

winControl.userRating: myData.rating}">

</div>

Notice that data-win-control, data-win-options, and data-win-bind can be used together and UI.process[All] and Binding.processAll will pick up the appropriate attributes. It’s important, however, thatUI.process[All]has completed its work before calling Binding.processAllas the latter depends on the control being instantiated. Typically, then, you’ll call Binding.processAll within the completed handler for UI.process[All]:

args.setPromise(WinJS.UI.processAll().then(function () {

WinJS.Binding.processAll(document.getElementById("boundElement"));

}));

Within data-win-bind, array lookup for source properties using [ ] is not supported, though you can do that through an initializer. Array lookup is supported on the target side, as is dot notation. Also, if the target object has a property that you want to refer to using a hyphenated identifier (where dot notation won’t work), you can use the following syntax:

<span data-win-bind="this[hyphenated-property']: source"></span>

That is, the target property string in data-win-bind basically carries through into the binding code that Binding.processAll generates; just as you can use this['hyphenated-property'] in code (this being the data context), you can use it in data-win-bind.

A similar syntax is necessary for binding attributes of the target element, such as the aria-* attributes for accessibility. As you probably know, attributes aren’t directly accessible through JavaScript properties on an element: they’re set through the element.setAttribute method instead. So you’d need to insert a converter into the data binding process to perform that particular step. Fortunately, WinJS.Binding includes initializers that do exactly this, initializer, setAttribute (for one-way binding) and setAttributeOneTime (for one-time binding). These are used as follows:

<label data-win-bind="this['aria-label']: title WinJS.Binding.setAttribute"></label>

<label data-win-bind="this['aria-label']: title

WinJS.Binding.setAttributeOneTime"></label>

One-Way Binding

The goal for one-way binding is, again, to update a target property, typically in a UI control, when the bound source property changes. One-way binding means to effectively repeat the one-time binding process whenever the source property changes.

In the previous section’s code, if we changed login.name after calling Binding.processAll, nothing will change in the output UI. So how can we automatically update the output?

Generally speaking, this requires that the data source maintains a list of bindings, where each binding describes a source property, a target property, and any associated converter function. The data source also needs to provide methods to manage that list, like addBinding, removeBinding, and so forth. Thirdly, whenever one of its bindable properties changes it goes through its list of bindings and updates any affected target property accordingly. All together, this makes what we call an observable source.

These requirements are quite generic; you can imagine that their implementation would pretty much join the ranks of classic boilerplate code. So, of course, WinJS.Binding provides just such an implementation with two functions at its core!

• Binding.as wraps any arbitrary object with an observable structure. (It’s a no-op for nonobjects, and Binding.unwrapremoves that structure if there’s a need.)

Binding.define creates a constructor for observable objects around a set of properties (described by a kind of empty object that just has property names). Such a constructor allows you to instantiate source objects dynamically, as when processing data retrieved from an online service.

Let’s see some code. Going back to the last BindingTests example above (Test 3), any time before or after Binding.processAll we can take the LoginData.login object and make it observable with just one line of code:

var loginObservable = WinJS.Binding.as(LoginData.login)

With everything else the same as before, we can now change a bound property within the loginObservable object:

loginObservable.name = "liambro";

This will update the target property (the mechanics of which we’ll talk about under “Under the Covers: Binding mixins” a little later):

images

Here’s how we’d then create and use a reusable class for an observable object (Test 4 in the BindingTests example). Notice the object we pass to Binding.define contains property names, but no values (they’ll be ignored in any case):

WinJS.Namespace.define("LoginData", {

//...

//LoginClass is a constructor for observable objects with the specified properties

LoginClass: WinJS.Binding.define({name: "", id: "", photoURL: "", userType: "" }),

});

We can now create instances of LoginClass, initializing desired properties with values. In this example, we’re using a different picture and leaving userType uninitialized:

var login4 = new LoginData.LoginClass({ name: "liamb",

photoURL: "http://www.kraigbrockschmidt.com/images/Liam08.png" });

Binding to this login object, the username initially comes out black.

//Do the binding (initial color of name would be black)

WinJS.Binding.processAll(document.getElementById("loginDisplay"), login4);

Updating the userType property will then cause an update the color of the target property, which happens through the converter automatically. In the example, this line of code is executed after a two-second delay so that you can see it change when you run the app:

login4.userType = "kid";

images

Sidebar: Binding to WinRT Objects

Although it is possible to declaratively bind directly to WinRT source objects, those objects support only one-time binding. The underlying reason for this is that each WinRT object is represented in JavaScript by a thin proxy that’s linked to that object instance, but callingWinJS.Binding.as on this proxy does not make the WinRT observable within the rest of the WinJS binding mechanisms. For this reason, it’s necessary to enforce one-time binding when using such sources directly. This can be done by specifying the WinJS oneTime initializer within eachdata-win-bind relationship, or specifying oneTime as the default initializer for processAll (the fifth argument). For example:

WinJS.Binding.processAll(element, winRTSourceObject, false, null, WinJS.Binding.oneTime);

As in the previous sidebar, if you’re binding to element attributes, you’ll need to use the setAttributeOneTime initializer instead.

To work around this limitation, you can create a custom proxy in JavaScript that watches changes in the WinRT source and copies those values to properties in the proxy. Because this proxy has its own set of properties, you can then make it observable with WinJS.Binding.as.

Implementing Two-Way Binding

As mentioned earlier, WinJS doesn’t presently include any facilities for two-way binding, but it’s straightforward to implement on your own:

1. Add listeners to the appropriate UI element events that relate to bound data source properties.

2. Within those handlers, update the data source properties.

The data source should be smart enough to know when the new value of the property is already the same as the target property, in which case it shouldn’t try to update the target lest you get caught in a loop. The observable object code that WinJS provides does this type of check for you.

To see an example of this, refer scenario 1 of the Declarative binding sample in the SDK, which listens for the change event on text boxes and updates values in its source accordingly. The input controls are declared as follows (html/1_BasicBinding.html, omitting much of the surrounding HTML structure):

<input type="text" id="basicBindingInputText"/>

<input type="text" id="basicBindingInputRed"/>

<input type="text" id="basicBindingInputGreen"/>

<input type="text" id="basicBindingInputBlue"/>

and the output elements like so:

<p>The text you entered was<span data-win-bind="innerHTML: text"></span>.

The value for red is<span data-win-bind="innerHTML: color.red"></span>,

the value for green is <span data-win-bind="innerHTML: color['green']"></span>,

and blueis <span data-win-bind="innerHTML: color.blue"></span>.

</p>

<p data-win-bind="style.background: color BasicBinding.toCssColor">

And here's your color as a background.</p>

The source object is defined in js/1_BasicBinding.js as a member of the surrounding page control:

this.bindingSource = {

text: "Initial text",

color: {

red: 128,

green: 128,

blue: 128

}

};

and is made observable through this line of code:

var b = WinJS.Binding.as(this.bindingSource);

You can see how its members are referenced in the data-win-bind attributes above, so that when we call processAll (on the div that contains all the output element):

WinJS.Binding.processAll(element.querySelector("#basicBindingOutput"), this.bindingSource);

we’ve set up one-way binding between the data source and the output. To complete two-way binding, we set up event handlers for the change event of each input field that take the value from the control and copy it back to the observable source:

this.bindTextBox("#basicBindingInputText", b.text,

function (value) { b.text = toStaticHTML(value); });

this.bindTextBox("#basicBindingInputRed", b.color.red,

function (value) { b.color.red = toStaticHTML(value); });

this.bindTextBox("#basicBindingInputGreen", b.color.green,

function (value) { b.color.green = toStaticHTML(value); });

this.bindTextBox("#basicBindingInputBlue", b.color.blue,

function (value) { b.color.blue = toStaticHTML(value); });

bindTextBox: function (selector, initialValue, setterCallback) {

var textBox = this.element.querySelector(selector);

textBox.addEventListener("change", function (evt) {

setterCallback(evt.target.value);

}, false);

textBox.value = initialValue;

}

In the HTML, you might have noticed an initializer called BasicBinding.toCssColor. This is defined in js/1_BasicBinding.js as follows:

var toCssColor = WinJS.Binding.initializer(

function toCssColor(source, sourceProperty, dest, destProperty) {

function setBackColor() {

dest.style.backgroundColor =

rgb(source.color.red, source.color.green,source.color.blue);

}

return WinJS.Binding.bind(source, {

color: {red: setBackColor,green: setBackColor,blue: setBackColor,}

});

}

);

// A little helper function to convert from separate rgb values to a css color

function rgb(r, g, b) { return"rgb(" + [r, g, b].join(",") + ")"; }

WinJS.Namespace.define("BasicBinding", {

toCssColor: toCssColor

});

Clearly, there’s more going on here than just creating a simple converter as we did before, including use of the methods WinJS.Binding.initializer and WinJS.Binding.bind. Now is a good time, then, to peek under the covers to see how initializers work and what functions like bind are doing.

Under the Covers: Binding mixins

Earlier in the “One-Way Binding” section I spelled out three requirements for an observable object: it maintains a list of bindings, provides methods to manage that list, and iterates through that list to update any target properties when source properties change. And we saw that Binding.asand Binding.define let you easily add such structures to a source object or class definition (you can find the source for both in the WinJS base.js file).

Both as and define create an observable object from an existing one by adding the necessary methods that the rest of WinJS.Binding requires. (This is done through mixins; to review details on WinJS.Class.mix and mixins, refer to Appendix B, “WinJS Extras.”)

The as method, for its part, assumes that the data source is just a plain object (and not a Date, Array, or nonobject) and creates a proxy object around it using an internal WinJS class called ObservableProxy. You wouldn’t instantiate this class directly, of course, but it’s instructive to look at its definition, where data is the object you give to as:

var ObservableProxy = WinJS.Class.mix(function (data) {

this._initObservable(data);

Object.defineProperties(this, expandProperties(data));

}, dynamicObservableMixin);

The call to _initObservable on the data source turns out to be very important. All it does is ensure that the class has a property called _backingData that’s set to the original source. Without this you’d see a bunch of exceptions at run time.

Binding.define does pretty much the same thing except that its purpose is to create a constructor for an observable object class, rather than just wrapping an existing instance. It’s how you define your own variant of the internal ObservableProxy class we see above.

You can also create an observable source manually, as shown in Scenario 3 of the Programmatic binding sample. Here it defines a RectangleSprite class with Class.define and then makes it observable (and adds a few more observable properties) as follows (js/3_CreatingBindableTypes.js):

WinJS.Class.mix(RectangleSprite,

WinJS.Binding.mixin,

WinJS.Binding.expandProperties({ position: 0, r: 0, g: 0, b: 0 })

);

Note that the RectangleSprite constructor in this case must call this._initObservable() or else none of the binding will work (that is, the app will crash and burn). This is the only case I know of where you need to explicitly call an underscore-named method in WinJS!

However you do it, though, the bottom line is that we’re creating a new class that combines the members defined by each argument to mix. In the cases above we have three such arguments: the original class, WinJS.Binding.mixin, and whatever object is produced byWinJS.Binding.expand-Properties.

expandProperties creates an object whose properties match those in the given object (the same names, but not the values), where each new property is wrapped in the proper structure for binding.53 Clearly, this type of operation is useful only when doing a mix, and it’s exactly whyBinding.define can digest an oddball, no-values object like the one we gave to it in Test 4 of the BindingTest example.

By “proper structure for binding,” I mean that each property has a get and set method (along with two properties names enumerable and configurable, both set to true). These methods rely on the class also having methods called getProperty and setProperty:

get: function () { returnthis.getProperty(propertyName); },

set: function (value) { this.setProperty(propertyName, value); },

It’s unlikely that any arbitrary class will contain such methods already, which is where the Binding.mixin object comes in. It contains a standard implementation of the binding functions, like [get | set]Property, that the rest of WinJS.Binding expects.

The mixin object itself an extension of the more basic Binding.observableMixin, which just contains three methods to manage a list of bindings:

• bind Saves a binding (property name and a listener function to invoke on change) into an internal list (an array). This is the binding equivalent of addEventListener.

• unbind Removes a binding created by bind (removing it from the list), or removes all bindings if a specific one isn’t given. This is the binding equivalent of removeEventListener.

• notify Goes through the bindings list for a property and asynchronously invokes its listeners with the new property value (at “normal” priority in the scheduler). notify will cancel any update that’s already in progress for the same property. This is the binding equivalent ofdispatchEvent.

The mixin object then adds five methods to manage properties through bind, unbind, and notify:

• setProperty Updates a property value and notifies listeners if the value changed.

• updateProperty Like setProperty, but returns a promise that completes when all listeners have been notified (the result in the promise is the new property value).

• getProperty Retrieves a property value as an observable object itself, which makes it possible to bind within nested object structures (obj1.obj2.prop3, etc.).

• addProperty Adds a new property to the object that is automatically enabled for binding (it adds the same properties and methods that that expandProperties does).

• removeProperty Removes a property altogether from the object.

The Binding.dynamicObservableMixin object, I should note, is exactly the same as mixin except for one small difference. If you take an observable class built with mixin and then derive a new class from it (see WinJS.Class.derive), the new class will not automatically be observable. If you build the base class with dynamicObservableMixin, on the other hand, its observability will apply to derived classes.

Programmatic Binding and WinJS.Binding.bind

Taking a step back from the details now, we can see that the eight mixin methods, along with the get and set implementations from expandProperties, fulfill the requirements of an observable data source. With such a source in hand you can do some interesting things programmatically. First, you can call the object’s bind method directly to hook up any number of additional actions manually. A couple of scenarios come to mind where this would apply:

• You set up two-way binding between a local data source and the app’s UI, and want to also sync changes to the source with a back-end service. A second handler attached through bind would be called whenever the source is modified through any other means.

• You need to create an intermediate source object that combines and consolidates property changes from other sources, simplifying binding to a final target UI element. Calling bind directly in such cases is necessary because Binding.processAll specifically works with UI elements declared in HTML rather than arbitrary JavaScript objects.

The notify method, for its part, is something you can call directly to trigger notifications. This is useful with additional bindings that don’t necessarily depend on the values themselves, just the fact that they changed. The major use case here is to implement computed properties—ones that change in response to another property value changing.

The WinJS binding engine also has some intelligent handling of multiple changes to the same source property. After the initial binding, further change notifications are asynchronous and multiple pending changes to the same property are coalesced. So, if in our example we made several changes to the name property in quick succession:

login.name = "Kenichiro";

login.name = "Josh";

login.name = "Chris";

only one notification for the last value would be sent and that would be the value that shows up in bound targets.

A couple of additional demonstrations of calling these binding methods directly can also be found in scenarios 1 and 2 of the Programmatic binding sample, which doesn’t use any declarative syntax at all. Scenario 1, for example, creates an observable source with Binding.as and wires it up with bind method to a couple of change handlers for two-way binding. (It employs the WinJS.Utilities.query and WinJS.Utilities.QueryCollection.listen methods, which are described in Appendix B.) Be mindful when looking at lines like this (js/1_BasicBinding.js):

this.bindSource.bind("x", this.onXChanged.bind(this));

that the first bind method on the source object comes from WinJS.Binding.mixin, whereas the bind method on the onXChanged function is the standard JavaScript method to manage the this variable!

Scenario 2 demonstrates a coding construct called a binding descriptor that works in conjunction with the static helper method WinJS.Binding.bind. Yes, be aware! WinJS.Binding.bind is yet another bind method that’s separate from a source object’s bind that’s defined in the mixins and separate from a function’s bind method. Fortunately, you must always call WinJS.Binding.bind with its full name, and I’ll refer to it this way to avoid confusion.

I call WinJS.Binding.bind a helper function, because it’s basically a way to make a bunch of bind calls for properties of a complex object. Consider the source object that’s created in scenario 2 of the Programmatic binding sample (js/2_BindingDescriptors.js):

this.objectPosition = WinJS.Binding.as({

position: { x: 10, y: 10},

color: { r: 128, g: 128, b: 128 }

}

If we wanted to set up binding relationships to each of these properties, we’d have to make a bunch of calls to this.objectPosition.bind like this:

this.objectPosition.bind(this.objectPosition.position.x, <action>)

Such code gets ugly to write in a hurry. A binding descriptor, then, succinctly expresses the property-action mappings in a structure that matches the source object. The generic syntax is: { property: { sub-property: function(value) { ... } } }. Here’s a concrete example from scenario 2 (js/2_BindingDescriptors.js):

{

position: {

x: onPositionChange,

y: onPositionChange

},

color: {

r: onColorChange,

g: onColorChange,

b: onColorChange

}

}

where onPositionChange and onColorChange both refresh the output in response to data changes.54 Scenario 1 of the Declarative binding sample has another example (js/1_BasicBinding.js):

{

color: {

red: setBackColor,

green: setBackColor,

blue: setBackColor,

}

}

When calling WinJS.Binding.bind, then, just pass the source object as the first argument and the binding descriptor as the second. As shown in the Declarative binding sample:

return WinJS.Binding.bind(source, {

color: {

red: setBackColor,

green: setBackColor,

blue: setBackColor,

}

});

The return value of WinJS.Binding.bind is an object with a cancel method that will clear out all these binding relationships (basically iterating through the structure calling unbind). In the Declarative binding sample, it returns this object from the binding initializer where it appears, which is actually very important. So let’s now turn to initializers.

Binding Initializers

When Binding.processAll encounters a data-win-bind attribute on an element, its main job is to take each <target property> : <source property>[<initializer>] string and turn it into a real binding relationship. Assuming that the data source is observable, this basically means calling the source’s bind method with the source property name and some handler that will update the target property accordingly.

The purpose of the initializer function in this process is to define exactly what happens in that handler—that is, what happens to the target property in response to a source property update. In essence, an initializer provides the body of the handler given to the source’s bind. In a simple binding relationship, that code might simply copy the source value to the target or it might involve a converter function. It could also consolidate multiple source properties—you can really do anything you want here. The key thing to remember is that the initializer itself will be called once and only once for each binding relationship, but the code it provides, such as a converter function, will be called every time the source property changes.

Now if you don’t specify a custom initializer within data-win-bind, WinJS will always use a default, namely WinJS.Binding.defaultBind.55 It simply sets up a binding relationship that copies the source property’s value straight over to the target property, as you’d expect. In short, the following two binding declarations have identical 'margin-top:12.0pt;margin-right:0cm;margin-bottom: 12.0pt;margin-left:0cm;line-height:normal'>data-win-bind="innerText: name"

data-win-bind="innerText: name defaultBind"

WinJS.Binding provides a number of other built-in initializers that can come in handy, most of which we’ve already encountered:

oneTime Performs a one-time copy of the source property to the target property without setting up any other binding relationship. This is necessary when the source is a WinRT object, as noted earlier in “Sidebar: Binding to WinRT Objects.”

setAttribute and setAttributeOneTime Similar to defaultBind and oneTime but injects a call to the target element’s setAttribute method instead of just copying the source value to a target property. See “Sidebar: Additional Property Syntax and Binding to WinJS Controls” earlier for more details.

addClassOneTime Like setAttributeOneTime except that it interprets the source property as a class name and thus calls the target element’s classList.add method to apply that class. This is useful when working with templates, which are described later in this chapter, because you can assign static classes to elements with the class attribute and then apply additional data-bound classes with this initializer.

Beyond these, we enter into the realm of custom initializers. The most common and simplest case is when you need to inject a converter into the binding relationship. All this takes on your part is to pass your conversion function to WinJS.Binding.converter, which returns the appropriate initializer (that’s also marked for declarative processing). We did this in Tests 2, 3 and 4 of the BindingTests example. For Tests 3 and 4, for example, the initializer LoginData.typeColorInitializer is created as follows:

WinJS.Namespace.define("LoginData", {

//...

typeColorInitializer: WinJS.Binding.converter(userTypeToColor)

});

which we use in the HTML like so:

<span id="loginName3"

data-win-bind="innerText: name; style.color: userType LoginData.typeColorInitializer">

</span>

Doing anything more requires that you implement the initializer function directly. This is necessary, for instance, if you need to apply a converter to a WinRT data source, where normally you’re required to use the oneTime initializer already. A custom initializer can then do both steps at once.

Scenario 1 of the Declarative binding sample gives us an example of an initializer function (js/1_BasicBinding.js):

var toCssColor = WinJS.Binding.initializer(

function toCssColor(source, sourceProperty, dest, destProperty) {

function setBackColor() {

dest.style.backgroundColor =

rgb(source.color.red, source.color.green,source.color.blue);

}

return WinJS.Binding.bind(source, {

color: {red: setBackColor,green: setBackColor,blue: setBackColor,}

});

}

);

// A little helper function to convert from separate rgb values to a css color

function rgb(r, g, b) { return"rgb(" + [r, g, b].join(",") + ")"; }

WinJS.Namespace.define("BasicBinding", {

toCssColor: toCssColor

});

WinJS.Binding.initializer is just an alias for WinJS.Utilities.markSupportedFor-Processing, as discussed in Chapter 4, “Web Content and Services.” It makes sure you can reference this initializer from processAll and doesn’t add anything else where binding is concerned.

The arguments passed to your initializer clearly tell you which properties of source and target (dest) are involved in this particular relationship. The source and dest arguments are the same as the dataContext and rootElement arguments given to processAll, respectively. ThesourceProperty and destProperty arguments are both arrays that contain the “paths” to their respective properties, where each part of the identifier is an element in the array. That is, if you have an identifier like style.color in data-win-bind, the path array will be [style, color].

Tip If you find it tricky to set a breakpoint inside an initializer, just insert the debugger statement at the top and you’ll always stop at that point.

With all this information in hand, you can then set up whatever binding relationships you want by calling the source’s bind method with whatever properties and handlers you require. To duplicate the behavior of oneTime, as with WinRT sources, you wouldn’t call bind but instead just assign the value of the source property to the destination property.

Although sourceProperty is what’s present in the data-win-bind attribute, you’re free to wire up to any other property you want to include in the relationship. The code above, for example, will be called with sourceProperty equal to [color], but it doesn’t actually bind to that directly. It instead uses a binding descriptor with WinJS.Binding.bind to hook up the setBackColor handler to three separate color subproperties. Although setBackColor is used for all three, you could just as easily have separate handlers for each one if, for example, each required a unique conversion.

Ultimately, what’s important is that the handler given to the source.bind method performs an appropriate update on the target object. In the code above, you can see that setBackColor sets dest.style.backgroundColor.

Hmmm. Do you see a problem there? Try changing the last data-win-bind attribute in html/1_BasicBinding.html to set the text color instead:

data-win-bind="style.color : color BasicBinding.toCssColor"

Oops! It still changes the background color! This is because the initializer isn’t honoring destProperty, and that would be a difficult bug to track down when it didn’t work as expected. Indeed, because the initializer pays no attention to destProperty you can use a nonexistent identifier and it will still change the background color:

data-win-bind="some.ridiculous.identifier : color BasicBinding.toCssColor"

Technically speaking, then, we could rewrite the code as follows:

dest.[destProperty[0]].[destProperty[1]] =

rgb(source.color.red, source.color.green, source.color.blue);

Even this code makes the assumption that the target path has only two components—to be really proper about it, you need to iterate through the array and traverse each step of the path. Fortunately, WinJS.Utilities.getMember is a helper for just that purpose, though to do an assignment you need to pop the last property from the array so that you can use [ ] to reference it:

var lastProp = destProperty.pop();

var target = destProperty.length ?

WinJS.Utilities.getMember(destProperty.join("."), dest) : dest;

destProperty.push(lastProp);

function setBackColor() {

target[lastProp] = rgb(source.color.red, source.color.green, source.color.blue);

}

This code can be found in the modified Declarative binding sample in this chapter’s companion content. Note that I’m building that reference outside of the setBackColor handler because there’s no need to rebuild it every time a source property changes. Also, calling push to restoredestProperty is important in scenario 3 when this initializer is used with a template that’s rendered multiple times on the same page. In that case the same destProperty array is used with each call to the initializer, meaning that we don’t want to make any permanent changes.

Remember also that sourceProperty can also contain multiple parts, so you may need to evaluate that path as well. The sample gets away with ignoring sourceProperty because it knows it’s only using the toCssColor initializer with source.color to begin with. Still, if you’re going to write an initializer, best to make it as robust as you can! Again, you can use getMember to assemble the reference you need, and because you’re just retrieving the value, you can do it in one line:

var value = WinJS.Utilities.getMember(sourceProperty.join("."), source);

Binding Templates

Now that we understand how controls work from Chapter 5 and how data binding works to populate those controls from a data source, the next question to ask is how we might define reusable pieces of HTML that also integrate declarative data binding.

There are two ways to do this. First, you can certainly use data-win-bind syntax within an HtmlControl or a custom control (remembering to bind control properties through winControl rather than the root element). Once the control is instantiated with WinJS.UI.process[All], you can then call WinJS.Binding.processAll to bind each element to an appropriate source.

The second way is through the WinJS.Binding.Template control. It provides a way to define an HTML snippet, either inline or in a separate HTML file, and then render that snippet, however many times you want, with whatever data source you need for each rendering. The template makes the call to Binding.processAll automatically and provides some other options where binding is concerned.

To define a template control, first create a div with data-win-control= "WinJS.Binding.Template" and an optional data-win-options string. The contents of the template—which are copied into the DOM when you render the template—can then be defined either inline or in a separate file. In that markup you’re free to use whatever controls you want along with data-win-bind attributes (as well as data-win-res attributes for localization).

Tip Blend for Visual Studio 2013 has some helpful features to easily create templates and data bindings from a data source for WinJS collection controls. We’ll see this in Chapter 7.

Template designs For an extensive collection of premade templates (designed primarily for the ListView control, but a great reference nonetheless), see Item templates for grid layouts and Item templates for list layouts.

Here’s an example from scenario 2 of the modified Declarative binding sample in this chapter’s companion content (html/2_TemplateControl.html, where the toCssColor initializer is corrected as we did earlier for scenario 1):

<div id="templateControlTemplate" data-win-control="WinJS.Binding.Template">

<!-- Bind both the background and the aria-label HTML attribute for the div -->

<div class="templateControlRenderedItem"

data-win-bind="style.background: color TemplateControl.toCssColor;

this['aria-label']: text WinJS.Binding.setAttribute" role="article">

<ol>

<li data-win-bind="textContent: text"></li>

<li><span class="templateControlTemplateLabel">r:

</span><span data-win-bind="textContent: color.r"></span></li>

<li><span class="templateControlTemplateLabel">g:

</span><span data-win-bind="textContent: color.g"></span></li>

<li><span class="templateControlTemplateLabel">b:

</span><span data-win-bind="textContent: color.b"></span></li>

</ol>

</div>

</div>

Scenario 3 of the example, which is an addition I’ve made to the original sample, has the same template contents stored in html/3_TemplateContents.html (<!DOCTYPE html>, <html>, and <body> tags are optional and have no effect). We then refer to those contents with the control’s hrefoption in data-win-options (html/3_TemplateControlHref.html):

<div id="templateControlTemplate" data-win-control="WinJS.Binding.Template"

data-win-options="{ href : '/html/3_TemplateContents.html' }">

</div>

Note Using the href option disables certain optimizations with templates that dramatically improve performance. You should only use href, then, when absolutely necessary.

What’s unique about this control is that it automatically hides itself (setting style.display="none") inside its constructor so that its contents never appear in your layout. You can confirm this when you run scenarios 2 or 3 of the example—if you expand everything in Visual Studio’s DOM Explorer, you won’t be able to find the template control anywhere. The control instance, however, is still in memory: a quick getElementById or querySelector will get you to the hidden root element, and that element’s winControl property is where you’ll find the WinJS template object.

That object has a number of methods and properties that we’ll return to in a bit. The most important of these is the asynchronous render method. Given whatever data source you want to bind to, render returns a promise that’s fulfilled with a copy of the template’s contents in a new element, where Binding.processAll has already been called with the data source. You can then attach that element to the DOM wherever you’d like.

Scenario 2 of the example, for instance, renders the template three times, binding each to a different source object that contains text and a color. First, in html/2_TemplateControl.html, the target div for the rendered templates is initially empty:

<div id="templateControlRenderTarget"></div>

In js/2_TemplateControl.js, we define the observable data sources as follows (showing WinJS.Binding.define):

var DataSource = WinJS.Binding.define({

text: "",color: { r: 0, g: 0, b: 0 }

});

// In the page control's init method

this.sourceObjects = [

new DataSource({ text: "First object", color: { r: 192, g: 64, b: 64 } }),

new DataSource({ text: "Second object", color: { r: 64, g: 192, b: 64 } }),

new DataSource({ text: "Third object", color: { r: 51, g: 153, b: 255 } })

];

In the page’s ready method, which is called after the page is rendered (meaning UI.processAll has instantiated the template), we do a bunch of work to wire up two-way binding and then render the template for each data source:

var templateControl = element.querySelector("#templateControlTemplate").winControl;

var renderHere = element.querySelector("#templateControlRenderTarget");

this.sourceObjects.forEach(function (o) {

templateControl.render(o).then(function (e) {

renderHere.appendChild(e);

});

});

Again, the template control is hidden but still present in the DOM, so we can get to it with the querySelector call. We then get the target div and iterate through the data sources, rendering the template with each source in turn, and attaching the resulting element tree (in e) to the target. The result of all this is shown below:

images

Note that render will, by default, create an extra container div for the template contents. That is, the output above will have this structure:

<div id="templateControlRenderTarget">

<div class="win-template win-disposable">

<!-- Template contents, starting with <div class="templateControlRenderedItem"> -->

</div>

<div class="win-template win-disposable">

<!-- ... -->

</div>

<div class="win-template win-disposable">

<!-- ... -->

</div>

</div>

You can eliminate that extra div by setting the template’s extractChild option to true:

data-win-options="{ extractChild : true }"

Also keep in mind that render calls Binding.processAll as part of its process, using, of course, the source you gave to render. Because there’s no point to process the template’s root element—the one with the data-win-control= "WinJS.Binding.Template"—the call to processAll has theskipRoot argument set to true. Internally WinJS also uses the bindingCache argument to optimize repeated renderings. That’s really what those arguments are there for.

As you can see, binding templates are quite straightforward to use. They will come in very handy when we talk about collection controls in the next chapter, because you can just point those controls to a template to use with each item in the collection. And with the WinJS.UI.Repeatercontrol, we’ll even see how you can use nested templates.

Sidebar: The Static WinJS.Binding.Template.render method

Within the WinJS.Binding.Template API is an odd duck of a method that’s documented as render.value. This is actually just a static method whose actual name in code is WinJS.Binding.Template.render and whose implementation is simply the following:

value: function (href, dataContext, container) {

returnnew WinJS.Binding.Template(null, { href: href }).render(dataContext, container);

}

This provides a programmatic shortcut for declaring a template with an href option, calling WinJS.UI.processAll to instantiate it, and then obtaining the control object and calling its render method. In short, WinJS.Binding.Template.render, which you’ll always call with the full identifier, is a one-line wonder for quickly rendering a file-based template with some data source into a container element. Scenario 4 of the modified Declarative Binding sample in this chapter’s companion content has a quick demonstration. The caveat is that the template will be loaded from disk each time you make this call, so it’s really best for one-shot renderings. If you’ll be using a template in multiple places, use an inline declaration so that the template object is present in the DOM for the lifetime of the page.

Template Options, Properties, and Compilation

Now that we’ve seen the basics of the Template control, let’s see what other features it supports. First are the options for the constructor that you can include in data-win-options:56

• href Specifies the path of an in-package HTML file that contains the template definition, as we’ve seen. Using this implicitly sets disableOptimizedProcessing to true.

• bindingInitializer Specifies a binding initializer to apply as the default to all data-win-bind relationships. This does not override any explicit initializer given in data-win-bind.

• debugBreakOnRender Executes a debugger statement whenever the template is first rendered and especially gives you a hook into a compiled template (see below). This is essential when a template’s render call is being made implicitly from within another control like the ListView.

• disableOptimizedProcessing Turns off template compiling; see below.

• extractChild If set to true, suppresses creation of a containing div for the template contents, as discussed in the previous section. The default is false.

All of these except for href can be accessed at run time through properties with the same names on the control: bindingInitializer, debugBreakOnRender, disableOptimizedProcessing, and extractChild.57 Note that changing any of these properties at run time will reset the template and cause it to be recompiled the next time it’s used.

Related to the behavior of the extractChild option is a second argument to the render method. If you already have a container into which you want the template contents rendered, you can provide it through this argument. For example, with extractChild set to true, the following code in scenario 3 of the modified Declarative binding sample (in the companion content) produces the same result as the default behavior (js/3_TemplateControlHRef.js):

this.sourceObjects.forEach(function (o) {

var container = document.createElement("div");

WinJS.Utilities.addClass(container, "win-template win-disposable");

renderHere.appendChild(container);

templateControl.render(o, container);

});

Such code gives you complete control over the kind of container element that’s created for the template, if you have need of it.

As for disableOptimizedProcessing and debugBreakOnRender, these relate to a significant change for WinJS 2.0 (in Windows 8.1): template compilation. In WinJS 1.0, template rendering was always done in an interpreted manner, meaning that each time you rendered a template, WinJS would parse your template’s markup and create each element in turn. You could improve this process by creating a rendering function directly, but you then lost the advantages of declarative markup.

To improve performance of declarative templates, especially with the ListView control, WinJS 2.0 compiles each template upon first rendering. This step dynamically creates a rendering function that makes subsequent use of the template much faster. In fact, if you have a Windows 8 (WinJS 1.0) app that uses the ListView control and you simply migrate the project to Windows 8.1 (WinJS 2.0), you’ll probably find it performing much better than before with no other changes.

Of course, with such extensive restructuring of the template engine there’s always the possibility that it breaks some existing code somewhere—perhaps yours! If you find that’s the case, or if for some reason want your app to just run slower, set disableOptimizedProcessing to true to bypass compilation and use the WinJS 1.0 rendering behavior. And again, note that using href to refer to template content will implicitly disable compilation.

With compiled templates, the WinJS engineers found it very helpful to have a hook into the dynamically-generated code, so they included the debugBreakOnRender option. If set to true (try it in scenario 2) and you run an app in the debugger, you’ll hit a debugger statement in code like this:

// generated template rendering function

returnfunction render(data, container) {

if (++sv23_debugCounter === 1) { debugger; }

if (typeof data === "object"&&typeof data.then === "function") {

// async data + a container falls back to interpreted path

If you step through this code you can see the nature of the compiled template. The HTML contents are added through a single insertAdjacentHtml with a string, for instance, and the steps that would normally happen within the async Binding.processAll, including calls to initializers, are done inline, so there’s no extra parsing of data-win-bind attributes. The same is true for WinJS controls in the template that would normally go through the async UI.processAllmethod: they are instead instantiated directly with new and a pre-parsed options object. Avoiding async calls and eliminating extra parsing clearly make compiled templates run much faster.

On the other hand, if you’re using href or have disableOptimizedProcessing turned on (as in scenario 3 of the example), you’ll instead hit a debugger statement inside this internal WinJS function:

function interpretedRender(template, dataContext, container) {

if (++template._counter === 1

&& (template.debugBreakOnRender || WinJS.Binding.Template._debugBreakOnRender)) {

debugger;

}

Stepping through this code you’ll see just how much more work is going on, such as a call to WinJS.UI.Fragments.renderCopy to asynchronously load up the template contents. Inside the completed handler for this (a variable called renderComplete), you’ll also see calls to the asyncUI.processAll and Binding.processAll methods.

If you continue looking through both renderers (compiled or interpreted), you’ll see they return an object with two properties called element and renderComplete. element is a promise that’s fulfilled when the element tree has been built up in memory, and renderComplete is a function that’s called once the promise is fulfilled (where data binding can then happen). This isn’t at all important for using the Template object but becomes important with collection controls like the ListView when you want to do performance optimization through your own rendering functions. We’ll see all that in Chapter 7.

Collection Data Types

No sooner had I sat down to start this section than my wife called me about the shopping list she’d left lying on our kitchen counter. It was a timely reminder that much of our reality where data is concerned is oriented not around single items or object instances, as we’ve dealt with thus far in this chapter, but around collections of such things. And as I said in the introduction to this chapter, dealing with collections and presenting them in an app’s UI is where data binding becomes exceptional useful. Otherwise you’d become very proficient (though I imagine you are already!) at doing copy/paste with lots of redundant binding code.

I assume that you’re already well familiar with the core collection type in JavaScript—the Array—whose various capabilities are all supported in Windows Store apps, as are the typed JavaScript collections like Uint16Array.

By itself, Array and typed arrays are not observable for data-binding purposes. Fortunately, WinJS provides the WinJS.Binding.List collection type that is easily built on an array. The List also supports creating grouped, sorted, and filtered projections of itself.

What also enters into the picture are specific language-neutral collection types that are used with WinRT APIs. In Chapter 4, for example, we encountered vectors in a number of places, such as network information, the credential locker, the content precacher, and background transfers. The other types are iterators, key-value pairs, maps, and property sets. As we’re talking about collections in this section, we’ll take the opportunity to familiarize ourselves with each of these constructs.

What’s most important to understand about the WinRT collections—especially where data binding is concerned—is how they project into the JavaScript environment, because that determines whether you can easily bind to them. Earlier we learned that it’s not possible to do one-way or two-way binding with WinRT objects, and these collections count as such. Fortunately, the JavaScript projection layer conveniently turns some of them into arrays, meaning that we can create a List and go from there.

Windows.Foundation.Collection Types

All of the WinRT collection types are found in the Windows.Foundation.Collections namespace. What you’ll notice immediately upon perusing the namespace is that it actually contains only one concrete class, PropertySet, and otherwise it is chock full of “interfaces”with curious names like IIterable<T>, IMapView<K, V>, and IVectorView<T>.

If you’ve at least fiddled with .NET languages like C# in your development career, you probably already know what the I and <T> business is all about because you get to type them out all the time. For the rest of you, it probably makes you appreciate the simplicity of JavaScript! In any case, let me explain a little more.

An interface, first of all, is an abstract definition of a group of related methods and properties. Interfaces in and of themselves have no implementation, so they’re sometimes referred to as abstract or virtualtypes. They simply describe a way to interact with some object that “implements” the interface, regardless of what else the object might do. Many objects, in fact, implement multiple interfaces. So anytime you see an I in front of some identifier, it’s just a shorthand for a group of members with some well-defined behavior.58

With collections in particular, it’s convenient to talk about the collection’s behavior independently from the particular object type that the collection contains. That is, whether you have an array of strings, integers, floats, or other complex objects, you still use the same methods to manipulate the array and the same properties like length are always there. The <T> shorthand is thus a way of saying “of type T” or “of whatever type the collection contains.” It certainly saves the documentation writers from having to copy and paste the same text into dozens of separate pages and do a search-and-replace on the data type! Of course, you’ll never encounter an abstract collection interface directly—in every instance you’ll see a concrete type in place of <T>. For instance, IVector<String> denotes a vector of strings: you navigate the collection through the methods of IVector, and the items in the collection are of type String.

In a few cases you’ll see <K> and <K, V> where K means key and V means value, both of which can also be of some arbitrary type. Again, it’s just a shorthand that enables us to talk about the behavior of the collection without getting caught up in the details of whatever object type it contains.

So that’s the syntactical sugar—let’s now look at the different WinRT collections.

Iterators

An iterator is the most basic form of a collection and one that maps directly to a simple array in JavaScript. The two iterator interfaces in WinRT are IIterable<T> and IIterator<T>. You’ll never work with these directly in JavaScript, however. For one thing, a JavaScript array (containing objects of any type) can be used with any WinRT API that wants an IIterable. Second, when a WinRT API produces an iterator as a result, you treat it as an array. (There are, in fact, no WinRT APIs available from JavaScript that directly produce a plain iterator; they produce vectors and maps that derive from IIterable and thus have those methods.)

In short, iterators are transparent from the JavaScript point of view, so when you see IIterable in the documentation for an API, just think “array.” For example, the first argument to Background-Downloader.requestUnconstrainedDownloadsAsync that we saw in Chapter 4 is documented as an IIterable<DownloadOperation>. In JavaScript this just means an array of DownloadOperationobjects; the JavaScript projection layer will make sure that the array is translated into an IIterable suitable for WinRT.

Similarly, a number of APIs in the Windows.Storage.FileIO and PathIO classes, such as appendLinesAsync and writeLinesAsync all take arguments of type IIterable<String>, which to us just means an array of strings.

Occasionally you’ll run into an API like ImageProperties.savePropertiesAsync (in Windows.-Storage.FileProperties) that takes an argument of type IIterable<IKeyValuePair>, which forces us to pause and ask just what we’re supposed to do with a collection interface of another interface! (IKeyValuePair<K, V> is also in Windows.Foundation.Collections.) Fortunately, the JavaScript projection layer translates IIterable<IKeyValuePair> into the concrete Windows.Foundation.-Collections.PropertySet class, which can be easily addressed as an array with named members, as we’ll see shortly.

Vectors and Vector Views

By itself, an iterator has methods to go through the collection in only one direction (IIterable.first returns an IIterator whose only methods are getMany and moveNext). A vector is a more capable variant (IVector derives from IIterable) that adds random-access methods (getAt andindexOf) and methods to modify the collection (append, clear, insertAt, removeAt, removeAtEnd, replaceAll, and setAt). A vector can also report its size.59

Because a vector is a type of iterator, you can also just treat it as a JavaScript array—a vector that you obtain from some WinRT API, that is, will have methods like forEach and splice as you’d expect. The one caveat here is that if you inspect a vector in Visual Studio’s debugger (as when you hover over a variable), you’ll see only its IVector members. But trust me, the others are there!

Vectors basically exist to help marshal changes to the collection between the JavaScript environment and WinRT. This is why IIterable is used as arguments to WinRT APIs (the input array is effectively read-only), whereas IVector is used as an output type, either for the return value of a synchronous API or for the results of an asynchronous API. For example, the readLinesAsync methods of the FileIO and PathIO methods in Windows.Storage provide results as a vector.

Within WinRT you’ll most often encounter vectors with a read-write collection property on some object. For example, the Windows.UI.Popups.MessageDialog object, which we’ll meet in Chapter 9, “Commanding UI,” has a commands property of type IVector<IUICommand>, which translates into JavaScript simply as an array of UICommand objects. Because the collection can be modified by either the app or the WinRT API, a vector is used to marshal those changes across the boundary.

In quite a number of cases—properties and methods alike—the collection involved is read-only but still needs to support random-access characteristics. For this we have the vector view (IVectorView also derives from IIterable), which doesn’t have the vector’s modification methods. To give a few examples, a vector view of ConnectionProfile objects comes back from NetworkInformation.-getConnectionProfiles (which we saw in Chapter 4). Similarly, StorageFolder.getFilesAsync provides a vector view of StorageFile objects. The user’s preferred languages in theWindows.-System.UserProfile.GlobalizationPreferences object is a vector view of strings. And you can always get a read-only view for any given read-write vector through the latter’s getView method.

Now because a vector is just a more capable iterator, guess what? You can just treat a vector like an array. For example, the Folder enumeration sample uses StorageFolder.getFilesAsync and getItemsAsync to list the contents of in your various media libraries, using forEach to iterate the array that those APIs produce. Using that example, here’s what I meant earlier when I mentioned that Visual Studio shows only the IVector members—the items from getItemsAsync doesn’t show array methods, but we can clearly call forEach (circled):

images

What all this means for data binding, to return to the context of this chapter, is that it’s no problem to create a WinJS.Binding.List from the WinRT methods and properties that produce vectors and vector views, because those collections are projected as arrays.

Tip If you’re enumerating folder contents to create a gallery experience—that is, displaying thumbnails of files in a control like the WinJS.UI.ListView—always use Windows.Storage.Storage-File.getThumbnailAsync or StorageFile.getScaledImageAsThumbnailAsync to retrieve a small image for your data source, which can be passed to URL.createObjectURL and the result assigned to an img.src attribute. This performs far better in both speed and memory efficiency, as the API draws from the thumbnails cache instead of loading the entire file contents (as happens if you call URL.createObjectURL on the full StorageFile). See the File and folder thumbnail sample for demonstrations.

Maps and Property Sets

A map (IMap<K, V>) and its read-only map view companion (IMapView<K, V>)are additional derivations of the basic iterator. A map is composed of key-value pairs, where the keys and values can both be arbitrary types.60 A closely related construct is the property set (IPropertySet), which relates to the map. The difference is that maps and map views are used (like vectors) to return collections from WinRT APIs and to handle certain properties of WinRT objects. Property sets, for their part, as used (like basic iterators) as input arguments to WinRT APIs.61

It’s important to note right up front that maps and property sets are not projected as arrays. They do support value lookup through the [ ] operator, but otherwise do not have array methods. A map, instead, has methods (from IMap) called lookup (same as [ ]), insert, remove, clear, andhasKey, along with a size property and a getView method like the vector. A map view shares hasKey, lookup, and size, and adds a split method. You can also pass a map or map view to Object.keys to retrieve an array of keys.

A property set has a more extensive interface, but let me come back to that in a bit.

The generic term for maps and property sets alike is a dictionary. A dictionary does just what the word means in colloquial language: it lets you look up an item (similar to a definition) in a collection (the dictionary) by a key (such as a word). This is very useful when the collection you’re working with doesn’t store items in a linear or indexed array, or doesn’t have a need to do so. For example, when working with the Windows.Storage.Pickers.FileSavePicker class, you give the user a choice of file types through its fileTypeChoices property. This property is interesting because its type is IMap<String, IVector>, meaning that it’s a map between a string key and a value that is itself an array. This makes sense, because you want to use something like “JPEG” for a key while yet mapping that single key to a group of values like “.jpg” and “.jpeg”.

Maps are also used extensively when working with file properties, where you have access to a deeper hierarchy of metadata that’s composed of key-value pairs. This is the same metadata that you can explore if you right-click a file in Windows Explorer and click the details tab, as shown here for a picture I took on a trip to Egypt where the camera recorded exposure details and so forth:

images

As we’ll see in Chapter 11, “The Story of State, Part 2,” the Windows.Storage.FileProperties API is how you get to all this metadata. Images provide an ImageProperties object, whose retrievePropertiesAsync produces a map containing the metadata. You can play with this in theSimple Imaging sample if you’d like, where once you’ve obtained a map you can access individual properties by name (retrievedProps is the map):

retrievedProps["System.Photo.ExposureTime"]

On the flip side of this metadata scene is where we encounter property sets. Again, these are a form of dictionary like maps but one that an app needs to create for input to methods like ImageProperties.savePropertiesAsync. This is why Windows.Foundation.Collections has a concretePropertySet class and not just an interface, because then we can new up an instance like so:

var properties = new Windows.Foundation.Collections.PropertySet();

and create key-value pairs with the [ ] operator:

properties["System.GPS.LatitudeNumerator"] = latNum;

or with the insert method:

properties.insert("System.GPS.LatitudeNumerator", latNum);

Be mindful that while the documentation for PropertySet lists quite a few methods and properties, only some of them are projected into JavaScript:

• Properties: size

• Lookup methods: [ ], lookup, hasKey, first (returns an iterator for the key-value pairs)

• Manipulation methods: clear, insert, remove

• Other: getView (retrieves a map view) and the mapChanged event

In summary, maps and property sets are distinct collection constructs that must be worked with through their own methods and properties. Vectors, on the other hand, are projected into JavaScript as an array and are thus suitable for data binding. If you want to bind to a map or property set, on the other hand, you’ll need to maintain an array copy.

WinJS Binding Lists

The WinJS.Binding.List object is the core of collection-based data binding in WinJS and is what you use with both templates and collection controls, as discussed in this chapter and the next. That is, the name of the ListView control is quite apt: it’s a view of a list but not the list itself. That list is a WinJS.Binding.List, which I’ll just refer to as List or “list” for convenience.

A List takes a JavaScript array and makes it observable, because such a process means more than WinJS.Binding.as does with a single object. The array in question can contain any kind of objects, from simple values to complex objects and other arrays. All it takes is a new:

list = new WinJS.Binding.List(dataArray);

Note that you can build a List with a sparse array—that is, an array with noncontiguous indices (see Using Arrays).

Once created, the List provides a number of array-like methods and properties, namely length, concat, every, filter, forEach, indexOf, join (using a comma), lastIndexOf, map, push, pop, reduce, reduceRight, reverse, shift, slice, sort, and unshift. These operate exactly like they do with a typical array except that functions like concat produce another List rather than an array. The List also provides additional lookup and management methods: getAt, getItem, getItemFromKey,indexOfKey, move, setAt, and some. I’ll leave you to check out the details of all these as needed, as they are all very simple and straightforward.

Hint You can create an empty List without an array, using new WinJS.Binding.List(). You then use methods like push to populate the list. Any data-binding relationships you set up with the empty list will remain in effect as you add items or remove and change existing items.

What’s much more interesting are those List features that apply more to data binding in particular, which come under four groups:

Constructor options The binding and proxy options affect whether items in the list are individually observable (binding) and whether the list uses the array directly for data storage (proxy).

Projections The createFiltered, createGrouped, and createSorted methods generate new List objects whose contents are controlled by filtering, grouping, and sorting functions, respectively.

Events As an observable object, the list can notify listeners when changes happen within its content. This is clearly important for any controls that are built on the List, such as the ListView. The supported events are itemchanged, iteminserted, itemmoved, itemmutated, itemremoved, andreload. (The List has the standard addEventListener, removeEvent-Listener, and dispatchEvent methods, and provides on* properties for the events.) Three additional methods—notify, notifyMutated, and notifyReload—will trigger appropriate events; notifyReload is generally called within operations like reverse and sort.

Binding The bind and unbind methods support binding to the list’s own properties (rather than those of its contents). The dataSource property supplies an “adapter” that’s specifically used by WinJS ListView and FlipView controls (see Chapter 7) or custom controls that want to use the same data model.

You’ll find that many Windows SDK samples use a List, especially those that deal with collection controls; there’s no shortage of examples. However, those samples are typically intertwined with the details of the control, so I’ve included the more fundamental BindingLists example in this chapter’s companion content to demonstrate two aspects of the List class: how the list relates to its underlying array (if one exists), and the various projections. These are the subjects of the next two sections.

Sidebar: Async Data Sources and Populating from a Feed

The implementation of List is wholly synchronous, meaning that significant operations on large data sets can block the UI thread. To avoid this, consider executing list-modifying code at a lower than normal priority using the WinJS.Utilities.Scheduler discussed in Chapter 3, “App Anatomy and Performance Fundamentals.” If you’re creating a custom control around a potentially large collection, consider using data sources that implement the IListDataSource interface as used by WinJS controls like the ListView. This interface accommodates asynchronous behavior as well as virtualization. See “Collection Control Data Sources” in Chapter 7.

If your collection isn’t so large that the synchronous nature of List will be a problem, you can still populate that list asynchronously such that items will automatically appear within data-bound controls. An excellent example of this (both virtualized and nonvirtualized cases) can be found in scenario 6 of the HTML FlipView control sample that we’ll meet in Chapter 7 and discuss in that chapter’s “Custom Data Sources and WinJS.UI.VirtualizedDataSource” section.

List-Array Relationships

By default, a List built on an array of simple values does not have any inherent relationship to the array. Scenario 1 of the BindingLists example in the companion content, for instance, creates a simple array of random numbers (this._array) and builds a list on it (this._list; js/scenario1.js):

this._list = new WinJS.Binding.List(this._array, this._options);

where this._options is optional and initially empty—more on this in a bit. Two buttons in this scenario then randomize the contents of the array and List separately. In both cases we iterate through the collection, using array[i] or List.setAt(i) to set the new numbers:

function randomizeArray(arr, num, lower, upper) {

for (var i = 0; i < num; i++) {

arr[i] = randInt(lower, upper);

}

}

function randomizeList(list, num, lower, upper) {

for (var i = 0; i < list.length; i++) {

list.setAt(i, randInt(lower, upper));

}

}

If you tap these buttons in scenario 1 (leaving the Proxy option unchecked for now), you’ll see that modifying the array does not update the List, nor does modifying the List update the array. Now let’s play with the checkboxes that recreate the list with its different constructor options:

images

In scenario 1 you’ll see that the Binding checkbox doesn’t do anything, which is expected. Now check the Proxy option and you’ll see that changes to the list now show up in the array because the list in this case is just a thin veneer over the array. Bear in mind, though, that if you then change the array, the list doesn’t get updated. This means it won’t fire any events and any other control that is built on the list will get badly out of sync with the real data. For this reason, it’s best to avoid changing the array directly when using the proxy option.

It’s very helpful to peek under the covers again and understand exactly what theList object is doing with the array, why the proxy works like it does, and what specific behaviors arise when object arrays are involved.

By default, with proxy set to false, the List creates an internal map of the array’s contents (no such map exists if the List is created without an array). Each entry in the map holds a key for an array item (typically its positional index as a string) and the item’s value, as shown in Figure 6-2.

images

FIGURE 6-2 The default relationship between a WinJS.Binding.List and its underlying array.

If the binding option is set to true, then the item in the map is the result of WinJS.Binding.as(<array_item>) instead, as shown in Figure 6-3.

images

FIGURE 6-3 The relationship between a WinJS.Binding.List and its underlying array with the binding option set to true. If the array is sparse, the keys will not be contiguous, of course.

In both of these cases, all of the list manipulation methods like concat, splice, reverse, sort, and so forth affect only the map—the array itself is left untouched.

That changes when the proxy option is set to true, because here the List no longer maintains a separate map but just holds a direct reference to the array, as shown in Figure 6-4. In this case, the binding option is ignored and the manipulation methods act directly on the array—they simply call the array’s methods of the same name. In this relationship it is obvious why changes to the List are reflected in the array, as scenario 1 of the example demonstrates.

images

FIGURE 6-4 The relationship between a WinJS.Binding.List and its underlying array with the proxy option set to true. The binding option is ignored in this case.

With proxy: true, this relationship holds regardless of whether the array contains simple values or complex objects. Note also in this case that when you change the array, methods like List.getAt will clearly return the updated value.

In contrast, when the List is using a map (proxy: false), something a little more interesting happens with an array of objects by virtue of this little bit of (condensed) code in the list’s constructor:

item = options.binding ? WinJS.Binding.as(inputArray[i]) : inputArray[i];

_keyMap[key] = { key: key, data: item }

If the item at inputArray[i] is a simple value, then the map will contain a copy of that value. However, if the item is an object, then the map will contain the item object itself (or an observable variant), rather than a copy. “So what?” you might ask. On the surface this doesn’t seem to mean much at all, but in fact it has two important implications (still assuming proxy: false):

• If you replace a whole item object in the array, the corresponding List entry will be unchanged because the original item object is still intact.

• If you change properties on an item object in the array, the properties of the corresponding List entry will also change.

This effect is demonstrated in scenario 2 of the BindingLists example. This scenario repeats the UI as scenario 1 but uses an array of objects rather than simple values. I have added an additional output element that’s data-bound to the first item in the list to show the effect of the bindingoption. The effect of the modification buttons and the Binding and Proxy checkboxes is the same as before. For example, with Proxy unchecked, the Modify Array Objects button replaces the array’s content with new objects like so (js/scenario2.js):

arr[i] = { number: randInt(lower, upper), color: randColor() };

but because the list’s map maintains references to the original items in the array, the List doesn’t see any changes (see the left side of Figure 6-5). Similarly, changing the list’s item through setAt:

list.setAt(i, { number: randInt(lower, upper), color: randColor() } );

replaces the whole object in the map with the new one, leaving the array unaffected (see the right side of Figure 6-5).

images

FIGURE 6-5 Replacing a whole object in either the array or the list will disconnect the related item in the other.

But now, with Proxy still unchecked, try the new button labeled Modify Array Object Properties, which instead modifies each array item this way:

arr[i].number = randInt(lower, upper);

arr[i].color = randColor();

Because we’re now modifying the existing objects in the array instead of replacing them, and because the list’s map contains references to those same objects, you’ll see that those changes are reflected automatically in the List (as well as the one element bound to a list item). This is illustrated on the left side of Figure 6-6. On the flip side, the Modify List Object Properties button calls List.getAt to obtain the item in the map and then changes its properties:

var item = list.getAt(i);

item.number = randInt(lower, upper);

item.color = randColor();

Because that’s still the same item as the one in the array, the array also sees those changes, as shown on the right side of Figure 6-6.

Note Tapping the Modify Array Objects or Modify List Objects buttons will replace all objects in the corresponding collection, disconnecting it from the other. The result is that modifying objects via properties will have no effect on the other collection. To restore the connection, re-create the List by changing one of the checkboxes.

images

FIGURE 6-6 Changing properties of objects in either the array or the list will change the properties in the other, because both are still referencing the same object.

All of this is important to keep in mind as you’re building more complex UI around a data source of some kind, whether using controls like the ListView or a custom control built on the List. That is, if you have code that modifies the array—or UI capabilities in the control to modify theList—you’ll know how the list and the array will be correspondingly affected. Otherwise you might be left scratching your head wondering just why certain changes to the list or array (item replacement) don’t affect the other whereas other changes (via properties) do!

Indeed, in scenario 2 you might notice that the span element that’s data-bound to the item from List.getAt(0) automatically updates when changes come through the List but not when they come through the array. This is because the binding mechanics are watching the list’s item, not the array item sitting beneath it. In such cases, be sure to make modifications through only the List.

It’s also good to know how the array and List relate when you start using projections of the List, because changes to the projections will propagate to the original List as well, as we’ll see next.

List Projections

The idea of a projection comes from the world of databases and data sources to mean a particular view of a data source that is still completely connected to that source. Projections are typically used to massage a raw data source into something more suitable for data binding to your UI, but without altering the original source.

For example, a data source for your personal contacts might have those contacts listed in any order and would of course contain the entire collection of those contacts. In an app’s UI, however, you probably want to sort and re-sort those contacts by one or more fields in each record. Each time you change the sort order in the UI, you’d create another sorted projection and bind the UI to it. Again, creating a projection doesn’t alter the data source. At the same time, changing properties of an item in the projection, inserting or removing items, or otherwise modifying the projection does propagate those changes back to the source. Similarly, if you add, remove, or change items in the underlying source—even through a projection—those changes propagate to all projections. In short, there is only ever one data source no matter how many projections are involved.

You might also want to present only a subset of your contacts in the UI based on certain filtering criteria. That’s called a filtered projection. You might also want to group those contacts by the first letter of the last name or by location. Whatever the case, projections make all this easier by applying such operations at the level of the data source so that you can just use the same UI and the same data binding code across the board.

Sorting, filtering, and grouping are the most common operations applied to data projections, and thus WinJS.Binding.List supports these through its createSorted, createFiltered, and createGrouped methods:

images

Performance tip If deriving the group key from an item at run time requires an involved process, you’ll improve overall performance by storing a prederived key in the item instead and just returning that from the group key function.

Globalization tip When sorting or otherwise comparing items, be sure to use globalization APIs like localeCompare to determine sort orders, or else you’ll really confuse your customers in different world markets! For grouping of textual items, also use theWindows.Globalization.Collation.-CharacterGroupings API. We’ll see an example in Chapter 7 in “Quickstart #4: The ListView Grouping Sample.”

Each projection generated by these methods is itself a special variant of the List class, meaning that each projection is individually bindable. You can also compose projections together. Calling List.createFiltered().createSorted(), for example, will first filter the original List and then apply sorting. List.createFiltered().createGrouped() will filter the List and then apply grouping (and sorting). It’s quite common, in fact, to filter a list first and then apply sorting and/or grouping because filtering is typically a less expensive operation than sorting or grouping.

When you create a grouped projection, it won’t necessarily call your grouping functions for every item right away. Instead, the projection will call those functions only when necessary, specifically when someone is asking the List or a projection for one or more items. This makes it possible to use projections with virtualized data sources where all the data isn’t necessarily in memory but loaded only when a control like the ListView is preparing a page of items.

Debugging tip You can of course set breakpoints within sorter, predicate, and grouping functions and step through the code a few times. With large and even modest collections, however, breakpoints quickly become tedious as these functions will be called many, many times! Instead, try using console.log or WinJS.log to emit the parameters received by these functions as well as their return values, allowing you to review the overall results much more quickly. In fact, it’s a great place to use WinJS.log with a tag specific to projections so that you can turn this specific logging output on and off independently.

Let’s see some examples now, drawing from scenario 3 of the BindingLists example in the companion content for the fundamentals. Trust me, we’ll see plenty more of projections in the next chapter when we work with collection controls!

Scenario 3 (js/scenario3.js) of the example creates an empty List without an underlying array and uses push to populate it with objects containing a random number and color (like scenario 2):

this._list = new WinJS.Binding.List();

for (var i = 0; i < num; i++) {

this._list.push({ number: randInt(this._rangeLower, this._rangeUpper), color: randColor() });

}

The output of this list is as follows:

images

It then creates two filtered projections, one that contains only those items whose key is greater than 50 and another that contains only those items where the blue of the color is greater than 192. The first filtering is done with an inline predicate, the second with a separate function. Note that in this and the following code I’ve omitted calls to WinJS.log and intermediate variables used for logging, and I’ll just show the output afterwards without additional comment:

this._filteredByNumber = this._list.createFiltered(function (j) {return j.number > 50;});

this._filteredByBlueness = this._list.createFiltered(filterBluenessOver192);

function filterBluenessOver192(j) {

return j.color.b > 192;

}

images

Then we have three sorted projections, one of the original list sorted by number (then blueness in case of repeats), one sorted just by blueness, and one sorted from the list filtered by blueness:

this._sorted = this._list.createSorted(sortByNumberThenBlueness);

this._sortedByBlueness = this._list.createSorted(sortByBlueness);

this._sortedByFilteredBlueness = this._filteredByBlueness.createSorted(sortByBlueness);

function sortByNumberThenBlueness (j, k) {

var result = j.number - k.number;

//If the items' numbers are the same, sort by blueness as the second tier

if (result == 0) {

result = sortByBlueness(j, k)

}

return result;

}

function sortByBlueness(j, k) {

return j.color.b - k.color.b;

}

images

Then we can add a projection that’s grouped by decades:

this._groupedByDecades = this._list.createGrouped(decadeKey, decadeGroupData.bind(this._list));

function decade(n) {return Math.floor(Math.floor(n / 10) * 10);}

function decadeKey(j) {

return decade(j.number);

}

//"this" will be bound to the list that's being grouped

function decadeGroupData(j) {

var dec = decade(j.number);

//Do a quick in-place filtering of the list so we can get the populationof the decade.

var decArray = this.filter(function (v) {return (decade(v.number) == dec);})

return {

decade: dec,

name: dec.toString(),

count: decArray.length

}

}

images

The last one is grouped by decades with the groups sorted in descending order, using the same key and group data functions as above:

this._groupedByDecadesSortedByCount =

this._list.createGrouped(decadeKey, decadeGroupData.bind(this._list),

//j and k are keys as returned from decadeKey; k-j does reverse sort

function (j, k) {return k - j;});

images

The Modify List button that’s included with this scenario demonstrates how the projections are always tied to the underlying List: when the list is repopulated with new values, you’ll see that all the projections get those new values as well. And if you look at the console output, you’ll see the log entries from the different sorting, filtering, and grouping functions. (You can turn logging off in js/default.js by removing the WinJS.Utilities.startLog calls.)

Tip When making many modifications to the root List, the group key, sorting, and filtering methods of the projections will be called repeatedly, but the groupData function won’t get called unless the groupKey function returns a key that doesn’t already exist. This means that the group data can get out of sync with the real list. For this reason I call the List.notifyReload method after repopulating the list to make sure that the projections are reset.

The Modify Projection button demonstrates that a change to a projection ripples through the other projections and the original list. In this case I just change the first item of the groupedByDecades-SortedByCount projection, which just goes to show that it doesn’t matter how many projection layers you have—you’re always talking to the same data source ultimately.

The code that generates the group data output answers a question you might have had earlier: what happens to the objects returned from the createGrouped method’s groupData function? Did all that just vanish into the netherworld? Not at all! Those objects simply ended up in the groupsproperty of the GroupedSortedListProjection object. This property is just another List (technically a GroupedListProjection object) that contains that group data. As a List you can bind UI to it, filter it, sort it, and so on. In the example, I just output its raw data along with its projection.

There’s one last detail to point out with the groupData function and the contents of the groups list: because you can return whatever objects you want from groupData, and because that function is called only once for each group, you can calculate other information like sums, counts—and really anything else!—and include that in the returned object. The List and its projections won’t care one way or the other. So if you want to communicate such information to the consumers of the grouped projection, the groupData function is the place to do it. This becomes very useful with collection controls like the ListView and especially the Semantic Zoom control that lets you switch between two lists with different levels of data. With semantic zoom, the zoomed-out view of a list typically shows group information and allows you to quickly navigate between groups. The objects you return from groupData are where you store any data you want to use in that view.

What We’ve Just Learned

• WinJS provides declarative data-binding capabilities for one-time and one-way binding, employing data-win-bind attributes and the WinJS.Binding.processAll method.

• WinJS.Binding mixins along with WinJS.Binding.as and WinJS.Binding.define simplify making arbitrary JavaScript objects observable and able to participate in data binding.

• With a little extra code to watch for changes in UI elements, an app can implement two-way binding.

• By default, data binding performs a simple copy between source and target properties. Through binding initializers, apps can customize the binding behavior, such as adding a conversion function or defining complex binding relationships between multiple properties.

• WinJS.Binding.Template controls provide a declarative means to easily define bits of HTML that can be repeatedly rendered with different source objects. Templates are heavily used in collection controls.

• In addition to the Array type of JavaScript, WinRT supports a number of other collection types such as the iterator, vector, map, and property set. Iterators and vectors project into JavaScript as an array and can be used with data binding through WinJS.Binding.List.

• WinJS.Binding.List provides an observable collection type that is a building block for collection controls. A List can be built from scratch or from an existing JavaScript array, as well as WinRT types that are projected as arrays.

• List projections provide filtering, sorting, and grouping capabilities on top of the original List without altering the list. As projections share the same data source as the original list, changes to items in the list or a projection will propagate to the list and all other projections.

51 More commonly, initializers and converters would be part of a namespace in which applicable UI elements are defined, because they’re more specific to the UI than to a data source.

52 A final comment that only warrants a footnote is that in Windows 8, apps typically added a line of code WinJS.Binding.optimizeBindingReferences = true to avoid some memory leaks. This is done automatically in WinJS 2.0 (for Windows 8.1), so that line is no longer necessary.

53expandProperties also includes properties of the object’s prototype.

54 The actual code uses this.onPositionChange.bind(this) which I’ve simplified to avoid confusion between all the binds!

55 As shown earlier in “Sidebar: Binding to WinRT Objects,” you can override the default initializer by providing your own as the fifth argument to processAll (after skipRoot and bindingCache). Though this argument doesn’t appear in the MSDN documentation, it is described within the WinJS source file and is safe to use.

56 Two other options, enableRecycling and processTimeout, are deprecated and for internal use, respectively.

57 One other property called isDeclarativeControlContainer is always set to true, and as with all other WinJS controls, the template has an element property that contains its root element in the DOM.

58 There are actually all kinds of interfaces floating around the WinRT API. When working in JavaScript, however, you rarely bump into them because the language doesn’t have such a formal construct. For example, a page control technically implements the WinJS.UI.Pages.IPageControlMembers interface, but you never see a direct reference to that name. Instead, you simply include its methods among the instance members of your page class. In contrast, when creating classes in other languages like C#, you explicitly list an interface as an abstract base class to inherit its methods.

59 The IObservableVector<T> interface in Windows.Foundations.Collections exists for other languages and is not ever seen in JavaScript.

60 Each pair is described by the IKeyValuePair<K, V> interface, but you don’t encounter that construct explicitly in JavaScript. Also, the IObservableMap<K, V> interface is also meant for other languages and not used in JavaScript.

61 In JavaScript, a property set is the only type of WinRT collection that you can create directly with the new operator. However, a few APIs that require a Map or IIterable argument have managed to slip through the API review process, thereby posing a problem for JavaScript developers. In these few cases, you have to find another method that returns the right type and then munge it to fit your needed input argument. See this forum post for an example of this workaround.