IT:AD:Knockout:HowTo:ClientSide View Custom BindingHandlers
Summary
The default value:xxx and text:xxx binding only goes so far.
Sometimes you need to transform the data between the viewModel and the display, and back again.
An example would be a JSON data value that needs to be converted from a string to a date, and then rendered without any time, for clean display via a JQuery datepicker.
In addition, any date entered has to be placed back into the model as a date.
Another example would be cleaning up data for dollars. On screen it has to have the prefix dollar symbol, but it has to be stripped out before it is mapped to a model numerical value.
Process
Edit: read
Example
//Dependencies:
//Nuget: moment.js
//A custom knockout binding:
//usage: <input ... data-bind="datevalue:MyModelsDate"/>
ko.bindingHandlers.datevalue = {
init: function (element, valueAccessor, allBindingsAccessor) {
// Use the value binding
ko.bindingHandlers.value.init(element, valueAccessor, allBindingsAccessor);
this.common(element, valueAccessor, allBindingsAccessor);
$(element).datepicker({ formatDate: dateFormat });
},
update: function (element, valueAccessor, allBindingsAccessor) {
// Use the value binding
ko.bindingHandlers.value.update(element, valueAccessor, allBindingsAccessor);
this.common(element, valueAccessor, allBindingsAccessor);
}
common : function(element, valueAccessor, allBindingsAccessor){
// Provide a custom text value
var value = valueAccessor();
var allBindings = allBindingsAccessor();
var dateFormat = allBindingsAccessor.dateFormat || "M/D/YYYY";
var strDate = ko.utils.unwrapObservable(value);
if (strDate) {
var date = moment(strDate).format(dateFormat);
$(element).val(date);
}
}
};
</sxh>
Another:
<sxh javascript>
//A custom knockout binding:
//usage: <input ... data-bind="dollarvalue:MyModelsDate"/>
ko.bindingHandlers.dollarvalue = {
init: function (element, valueAccessor, allBindingsAccessor) {
var underlyingObservable = valueAccessor();
var interceptor = ko.computed({
read: function () {
// this function does get called, but it's return value is not used as the value of the textbox.
// the raw value from the underlyingObservable, or the actual value the user entered is used instead, no
// dollar sign added. It seems like this read function is completely useless, and isn't used at all
return "$" + underlyingObservable();
},
write: function (newValue) {
var current = underlyingObservable(),
valueToWrite = Math.round(parseFloat(newValue.replace("$", "")) * 100) / 100;
if (valueToWrite !== current) {
// for some reason, if a user enters 20.00000 for example, the value written to the observable
// is 20, but the original value they entered (20.00000) is still shown in the text box.
underlyingObservable(valueToWrite);
} else {
if (newValue !== current.toString())
underlyingObservable.valueHasMutated();
}
}
});
ko.applyBindingsToNode(element, { value: interceptor });
}
};
Resources
* Docs: http://knockoutjs.com/documentation/custom-bindings.html * Docs: http://knockoutjs.com/documentation/extenders.html * Date, DateTime, Duration (good):
* dollarvalue: