Continuing from Part 1, to implement an binding initializer function directly looks like this, which is taken from scenario 1 of the Declarative binding sample (for 8.1):

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.markSupportedForProcessing, which 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 argument is the same as the dataContext given to processAll, and dest is the element on which this particular initializer was declared (which can be anywhere below the rootElement given to processAll). The sourceProperty 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].

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. Note that 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? In the Declarative binding sample, 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 use a non-existent 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. Here’s a way to do that:

var target = “dest”;
for (var i = 0, len = destProperty.length – 1; i < len; i++) {
target += “.” + destProperty[i];
}

target = eval(target);
var lastProp = destProperty[i];

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

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.

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!

 

[7/23/13: Corrected one statement above on the source and dest arguments to the initializer. It has incorrectly stated that dest was the same as the rootElement to processAll; it’s the element where the initializer is declared.]


Comments are closed