Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Knockout computed gives Function expected error in IE only

I'm getting a "SCRIPT5002: Function expected" that only happens in IE. I'm currently testing against version 9. It happens when I use a previously defined computed observable inside of another computed observable.

My application is a bit more complex than this, so I've reproduced the error with the much simpler code below. The error happens on the line z = self.subtotal(); when you enter a number in for Number 1, Number 2, and Number 3 (and tab out).

This error does not occur in Chrome or Firefox and I've googled for quite a while. Hopefully someone can help un-stick me.

Here is the link to the jsfiddle: http://jsfiddle.net/kCmTg/

Here is the javascript:

    function putVars() {
    self = this;
    self.number1 = ko.observable();
    self.number2 = ko.observable();
    self.subtotal = ko.computed(function () {
        return parseFloat(self.number1()) + parseFloat(self.number2());
    }, self, { deferEvaluation: true });

    self.number3 = ko.observable();
    self.number4 = ko.observable();
    self.total = ko.computed(function () {
        var x, y, z;
        x = self.number3();
        y = self.number4();
        z = self.subtotal();
        return parseFloat(x) + parseFloat(y) + parseFloat(z);
    }, self, { deferEvaluation: true });
}

$(function () {
    ko.applyBindings(new putVars());
});

Here is the html:

    <h4>Calc 1</h4>
<label for="Number1">Number 1: </label><input id="Number1" type="text" data-bind="value: number1" />
<label for="Number2">Number 2: </label><input id="Number2" type="text" data-bind="value: number2" />
<label for="Subtotal"><b>Subtotal: </b></label><input id="Subtotal" type="text" data-bind="value: subtotal" readonly="readonly" />
<hr />
<h4>Calc 2</h4>
<label for="Number3">Number 3: </label><input id="Number3" type="text" data-bind="value: number3" />
<label for="Number4">Number 4: </label><input id="Number4" type="text" data-bind="value: number4" />
<label for="Total"><b>Total:</b> </label><input id="Total" type="text" readonly="readonly" data-bind="value: total" />
like image 315
EJDev Avatar asked Mar 01 '13 19:03

EJDev


2 Answers

This has a similar cause to this one: knockout.js Computed observable called twice in Internet Explorer and is caused by the fact that in IE<10, Knockout has some special code to deal with getting an autocomplete value from the field. It does this even if the field is read-only as in your case. It does check, however, for the autocomplete attribute. So you could fix it like this:

<input id="Subtotal" type="text" data-bind="value: subtotal" autocomplete="off" readonly="readonly" />

There is also a bug in Knockout at play here--that it will overwrite a computed observable passed to a two-way binding. This is already fixed in the development version of Knockout to be released as version 2.3.0 (probably in April 2013). To work around this, you can pass the value of the observable to the binding instead of the computed observable itself, like this:

<input id="Subtotal" type="text" data-bind="value: subtotal()" readonly="readonly" />
like image 190
Michael Best Avatar answered Oct 04 '22 20:10

Michael Best


It appears you've discovered a bug in IE or KnockoutJS, likely exposed by Knockout's bindings, where Knockout is pushing a value into an observable, but in IE9, it overwrites the property.

This doesn't occur on IE10, suggesting it's a bug in IE9. I'd guess Knockout has something where it's checking if some value is a writable observable function and it's being reported incorrectly on IE9.

Interestingly, if you change KO computed to use a read/write, the error ceases:

self.subtotal = ko.computed({
     read: function() { 
         return parseFloat(this.number1()) + parseFloat(this.number2());
     },
     write: function(val) { }
}, self);

Perhaps that is a sufficient work-around?

like image 43
Judah Gabriel Himango Avatar answered Oct 04 '22 18:10

Judah Gabriel Himango