This is a question to try and resolve a UI caching issue with Knockout mentioned as an aside in this answer.
Take the below binding (based on the previous answer):
ko.bindingHandlers.amountValue = {
init: function (element, valueAccessor, allBindingsAccessor) {
var underlyingObservable = valueAccessor();
var interceptor = ko.computed({
read: function () {
return "$" + underlyingObservable();
},
write: function (newValue) {
var current = underlyingObservable(),
valueToWrite = Math.round(parseFloat(newValue.replace("$", "")) * 100) / 100;
if (valueToWrite === current) {
// Clear the observable's value to force a UI refresh
underlyingObservable(null);
}
underlyingObservable(valueToWrite);
}
});
ko.applyBindingsToNode(element, { value: interceptor });
}
};
The write function strips the dollar sign, converts it from a string to a number, and rounds the amount to 2 decimal places, storing the result in the observable.
However, if the user is entering a value which, after parsing/conversion, results in the same value as that currently stored in the underlying observable then there is a problem. Essentially, Knockout decides that as the value has not changed, therefore the UI need not change. The UI and the underlying observable are out of sync. THIS IS BAD.
Example: current underlying Amount 1 value is 22.45. User enters "$22.45111". On update the Amount 1 value is set as 22.45. However the on-screen field value is left as "$22.45111". i.e. It is out of sync with the underlying Amount 1 value.
I have a workaround for this issue which is also implemented in the above binding:
if (valueToWrite === current) {
// Clear the observable's value to force a UI refresh
underlyingObservable(null);
}
This checks if the valueToWrite
to the observable is the same as that currently stored in the observable and, if it is, clears that value out (by setting the observable to null). Then it subsequently sets the observable back to that old / new value which in turn refreshes the UI.
This works but it feels wrong. Surely there must be a better way? I'm assuming that I've overlooked something but I don't know what.
I don't believe that valueHasMutated
/ valueWillMutate
is the answer. Is there a forceUIRefresh
method hiding out in Knockout that I haven't spotted?
This is illustrated in this jsfiddle