I know that it's been answered before and here's the code for it (Knockout JS)
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;
};
But how can I handle these scenarios?
- User deletes the 0.00 in the text field, it should default back
to 0.00 instead of staying blank
- User types letters in the text
field, it should also default back to 0.00 instead of returning NaN
In your read
function, which generates the output, the NaN
is created here:
+observable() // The + tries to "cast" the observable's value to a number
To get rid of the NaN
, you'll have to do an isNaN
check:
var nr = +observable();
if (isNaN(nr)) {
nr = 0;
}
Now, in your write
method, which transforms the input, there's a null
value returned for invalid input. Replace this to 0
to default to $0.00
:
observable(isNaN(value) ? 0 : value);
If you can be sure the formatted
computed is the only one writing to the underlying observable, you only need to fix one of these (i.e. you decide to format values on inputting them to the system or on outputting them).
The snippet below shows these fixes linked to an input. Personally, I think this behavior is better suited for an extender, but I'm not sure if it matters.
ko.observable.fn.withCurrencyFormat = function (precision) {
var observable = this;
observable.formatted = ko.computed({
read: function (key) {
var nr = +observable();
if (isNaN(nr)) nr = 0;
return '$' + nr.toFixed(precision);
},
write: function (value) {
value = parseFloat(value.replace(/[^\.\d]/g, ""));
observable(isNaN(value) ? 0 : value);
}
}).extend({notify: 'always'});
return observable;
};
var obs = ko.observable(0).extend({notify: 'always'});
var val = obs.withCurrencyFormat(2).formatted;
ko.applyBindings({
val: val
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<input data-bind="value: val">