positive numeric value in binding

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="positiveNumericValue: age" />
    </div>
</script>

CSS

input { width: 75px; }

JavaScript

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
};


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);