Knockout.js: time input format and value restriction
Asked Answered
E

1

33

When I bind numeric data in my view model using knockout, it displays correctly but changes the data type to string if the user changes the input tag value. The problem with submitting string is that the server expects a numeric value with no implicit conversion available.

Any way to tell knockout to maintain the data type of the original property value?

My example code that matches the view model names to the input tag names. I use unobtrusive knockout to do the bindings, which works fine.

// Bind the first object returned to the first view model object
// FNS is the namespace, VM is the view model
FNS.VM.Items[0] = ko.mapping.fromJS(data.Items[0]);

// For each property found, find the matching input and bind it
$.each(FNS.VM.Items[0], function (indexInArray, valueOfElement) {
    var attrName = indexInArray;
    var attrValue;
    if (typeof valueOfElement == "function")
        attrValue = valueOfElement();
    else
        attrValue = valueOfElement;

    var a = $('input[name="' + attrName + '"][type="checkbox"]');
    if (a.length)
        a.dataBind({ checked: 'VM.Items[0].' + attrName });

    var b = $('input[name="' + attrName + '"][type="radio"]');
    if (b.length)
        b.dataBind({ checked: 'VM.Items[0].' + attrName });

    var c = $('input[name="' + attrName + '"][type="text"]');
    if (c.length)
        c.dataBind({ value: 'VM.Items[0].' + attrName });
});
ko.applyBindings(FNS);
Entitle answered 13/9, 2011 at 1:46 Comment(0)
O
50

Here is a thread with a few different techniques to keep the value numeric: https://groups.google.com/d/topic/knockoutjs/SPrzcgddoY4/discussion

One option is to push this concern into your view model and create a numericObservable to use instead of a normal observable. It might look like:

ko.numericObservable = function(initialValue) {
    var _actual = ko.observable(initialValue);

    var result = ko.dependentObservable({
        read: function() {
            return _actual();
        },
        write: function(newValue) {
            var parsedValue = parseFloat(newValue);
            _actual(isNaN(parsedValue) ? newValue : parsedValue);
        }
    });

    return result;
};

Sample: http://jsfiddle.net/rniemeyer/RJbdS/

Another option is to handle this with a custom binding. Instead of using the value binding, you can define a numericValue binding and use it instead. It could look like:

ko.bindingHandlers.numericValue = {
    init : function(element, valueAccessor, allBindings, data, context) {
        var interceptor = ko.computed({
            read: function() {
                return ko.unwrap(valueAccessor());
            },
            write: function(value) {
                if (!isNaN(value)) {
                    valueAccessor()(parseFloat(value));
                }                
            },
            disposeWhenNodeIsRemoved: element 
        });

        ko.applyBindingsToNode(element, { value: interceptor }, context);
    }
};

Sample: http://jsfiddle.net/rniemeyer/wtZ9X/

Omalley answered 13/9, 2011 at 1:59 Comment(7)
Your answer lead me to this answer under Writable dependentObservables, which collectively answers my question. ThanksEntitle
Quick note: Reading through the Knockout commits I see RP Niemeyer referenced his own answer here with a quick note: github.com/knockout/knockout/pull/1334 There's a more supported way of invoking the line ko.bindingHandlers.value.init...Hydrogenize
Anyone reading this, please note dependentObservable() is now called computed()Demona
Beware: Further to @IanYates binding comment. ko 3.2.0 kbh value breaks any use of update. ko.bindingHandlers.value.update calls are ignored. If you need to change the value read from the model, use the interceptor.read function. e.g. to coerce to a string return underlyingObservable().toString();Genuine
@Genuine - updated with an approach that is more compatible with current KO.Omalley
Is the disposeWhenNodeIsRemoved necessary in the ko.numericObservable? There is no element in scope so it will be undefined. I can see the case for the binding handler however which could probably use it.Partisan
@JeffMercado - Thanks- I inadvertently added it to the wrong block of code.Omalley

© 2022 - 2024 — McMap. All rights reserved.