For your specific scenario, one way to handle this is by creating a custom binding that is able to intercept the value and do your validation. This can be done by creating a writeable computed in the custom binding to bind against. The advantage is that you don't have to worry about the mapping plugin customizing your object creation.
It might look something like:
ko.bindingHandlers.positiveNumericValue = {
init : function(element, valueAccessor, allBindingsAccessor) {
var underlyingObservable = valueAccessor();
var interceptor = ko.computed({
read: underlyingObservable,
write: function(newValue) {
var current = underlyingObservable(),
valueToWrite = isNaN(newValue) ? 0 : parseFloat(+newValue);
if (valueToWrite < 0) {
valueToWrite = 0;
}
//only write if it changed
if (valueToWrite !== current) {
underlyingObservable(valueToWrite);
} else {
//if the rounded value is the same as it was, but a different value was written, force a notification so the current field is updated to the rounded value
if (newValue !== current) {
underlyingObservable.valueHasMutated();
}
}
}
});
ko.bindingHandlers.value.init(element, function() { return interceptor }, allBindingsAccessor);
},
update : ko.bindingHandlers.value.update
};
Here is a sample: http://jsfiddle.net/rniemeyer/2TnSM/
Another way would be to extend observables with an option to create the writeable computed.
For your scenario, it might look like:
ko.observable.fn.forcePositive = function() {
var underlyingObservable = this;
if (!this.forcePositiveInterceptor) {
this.forcePositiveInterceptor = ko.computed({
read: this,
write: function(newValue) {
var current = underlyingObservable(),
valueToWrite = isNaN(newValue) ? 0 : parseFloat(+newValue);
if (valueToWrite < 0) {
valueToWrite = 0;
}
//only write if it changed
if (valueToWrite !== current) {
underlyingObservable(valueToWrite);
} else {
//if the rounded value is the same as it was, but a different value was written, force a notification so the current field is updated to the rounded value
if (newValue !== current) {
underlyingObservable.valueHasMutated();
}
}
}
});
}
return this.forcePositiveInterceptor;
};
You would then bind against it like:
<input type="text" name="age" data-bind="value: age.forcePositive()" />
The way that I implemented it here, you would need to call is as a function forcePositive()
so the writeable is initialized. This is so that you can just use the mapping plugin without any customization and just do this on any observable that you want to use this functionality with.
Sample: http://jsfiddle.net/rniemeyer/Dy4MH/
I think that either choice would work for your scenario. I probably favor the second choice, so that you could add more of these while using the normal bindings.