Here are a couple ways that you can make this reusable.
If you want to handle this in your view model, then a good choice is to create an extension that will store the formatted computed observable as a "sub-observable" of your original. You can extend observables using extenders or by adding to the shared fn
object as described here. I prefer the latter.
So, you could add a function to observables called withCurrencyFormat
. It might look like:
ko.observable.fn.withCurrencyFormat = function(precision) {
var observable = this;
observable.formatted = ko.computed({
read: function (key) {
return '$' + (+observable()).toFixed(precision);
},
write: function (value) {
value = parseFloat(value.replace(/[^\.\d]/g, ""));
observable(isNaN(value) ? null : value); // Write to underlying storage
}
});
return observable;
};
Now, you can say:
self.week1Amount = ko.observable(w1).withCurrencyFormat(2);
self.week2Amount = ko.observable(w2).withCurrencyFormat(2);
self.week3Amount = ko.observable(w3).withCurrencyFormat(2);
and bind against it in the UI like:
<td><input data-bind="value: week1Amount.formatted" /></td>
<td><input data-bind="value: week2Amount.formatted" /></td>
<td><input data-bind="value: week3Amount.formatted" /></td>
Sample here: http://jsfiddle.net/rniemeyer/xskJN/
Another choice is to move this into a binding, so you can leave your view model alone. This would use similar code, but in a custom binding handler that might look like:
ko.bindingHandlers.valueAsCurrency = {
init: function(element, valueAccessor) {
var observable = valueAccessor(),
formatted = ko.computed({
read: function (key) {
return '$' + (+observable()).toFixed(2);
},
write: function (value) {
value = parseFloat(value.replace(/[^\.\d]/g, ""));
observable(isNaN(value) ? null : value); // Write to underlying storage
},
disposeWhenNodeIsRemoved: element
});
//apply the actual value binding with our new computed
ko.applyBindingsToNode(element, { value: formatted });
}
};
So, in the binding handler we are create our computed and then using the value
binding against it.
Now, your view model would need no changes and you would bind in the UI like:
<td><input data-bind="valueAsCurrency: week1Amount" /></td>
<td><input data-bind="valueAsCurrency: week2Amount" /></td>
<td><input data-bind="valueAsCurrency: week3Amount" /></td>
Sample here: http://jsfiddle.net/rniemeyer/sD6y4/