positive numeric value extend observable

by rniemeyer

HTML

<script src="http://knockoutjs.com/downloads/knockout-2.1.0.js"></script>
<div data-bind='template: { name: "personTemplate", foreach: persons }'>
    <hr/>
    <div data-bind="text: ko.toJSON(viewModel)"></div>
<script id='personTemplate' type='text/html'>
    <div>
        <input type="text" name="firstname" data-bind="value: firstname" />
        <input type="text" name="lastname" data-bind="value: lastname" />
        <input type="text" name="age" data-bind="value: age.forcePositive()" />
    </div>
</script>

CSS

input { width: 75px; }

JavaScript

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;        
};
                                      
                                      
                                      
var viewModel = {
    persons: ko.observableArray([{"firstname":"Kenneth","lastname":"None","age":ko.observable(37)},{"firstname":"Michael","lastname":"None","age":ko.observable(45)}]),
    save: function() {
            console.log('saving model with...');
            console.log(ko.toJS(viewModel));
    }
};
ko.applyBindings(viewModel);