Question

I'm trying to implement something like a typesafe ViewModel using KnockoutJS. It works pretty well until I start to update observables via HTML input tags.

I have implemented type extender which returns computed observable:

return ko.computed({
    read: target,
    write: fixer
})

Where fixer is something like:

function (newValue) {
    var current = target(),
        valueToWrite = (newValue == null ? null : fixNumber(newValue, 0));

    if (valueToWrite !== current) target(valueToWrite);
    else if (newValue !== current) target.notifySubscribers(valueToWrite);
}

And fixNumber is

function fixNumber(value, precision) {
    if (value == null || value === '') return null;

    var newValue = (value || '').toString().replace(/^[^\,\.\d\-]*([\.\,\-]?\d*)([\,\.]?\d*).*$/, '$1$2').replace(/\,/, '.'),
        valueToWrite = Number(newValue);

    return !!(valueToWrite % 1) ? round(valueToWrite, precision) : valueToWrite;
}

It looks not so straightforward, but I have to consider possible use of comma as a decimal separator.

Often I need to update my observables as soon as user presses key to reflect this change immediately:

<input type="text" data-bind="value: nonThrottled, valueUpdate: 'afterkeyup'"></input>

And here comes a lot of problems, because, for example, I can't input decimal values less than 1 (0.1, 0.2, etc) there.

When I try to throttle an observable it mostly works. But sometimes user input and type fixer go out of sync, so it looks like some input gets lost occasionally.

Full example is there http://jsfiddle.net/mailgpa/JHztW/. I would really appreciate any hints, since I have spent days trying to fix these problems.

UPDATE 11/04/2013

I solved my problem providing custom value binding, so now throttled observables doesn't eat my input occasionally.

I've added additional valueThrottle option-binding to throttle updating of element's value:

var valueThrottle = allBindingsAccessor()["valueThrottle"];
var valueThrottleTimeoutInstance = null;

/* ... */

if (valueThrottle) {
    clearTimeout(valueThrottleTimeoutInstance);
    valueThrottleTimeoutInstance = setTimeout(function () {
      ko.selectExtensions.writeValue( element, ko.utils.unwrapObservable(valueAccessor()) );
    }, valueThrottle);
  } else applyValueAction();

Also I've noticed that inability to enter values like 0.2 in my case comes from that statement in original value binding:

if ((newValue === 0) && (elementValue !== 0) && (elementValue !== "0"))
    valueHasChanged = true;

I've rewritten it as

if ((newValue === 0) && (elementValue != 0))
    valueHasChanged = true;

It works at least at Chrome, but I haven't tested it properly and even not sure that it's correct.

Example is to be added, for some reason jsFiddle does not accept my custom binding.

Any comments are really appreciated.

No correct solution

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top