Knockout computed gives Function expected error in IE only
Asked Answered
T

3

6

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" />
Trouveur answered 1/3, 2013 at 19:45 Comment(0)
P
7

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" />
Pagel answered 2/3, 2013 at 2:43 Comment(2)
Thanks for a bit more clarity on the issues and a little more succinct fix. I had already implemented Judah's solution before seeing this option and left it for other reasons that are irrelevant to this discussion. I do believe this is the more accurate answer though.Trouveur
Had a similar issue and was able to fix it by using your 2nd fix, putting the () on the value in the data-bind. Great job finding that.Gamin
F
3

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?

Faefaeces answered 1/3, 2013 at 22:31 Comment(1)
This work around will be absolutely fine. Thanks so much for your help!Trouveur
G
0

Please check to see if you observable has a value being passed as a parameter or not. for example budgetlineviewmodel.total = total(5). I have tried using budgetlineviewmodel.total = 5. but with no success.

Knockout Observables are nothing but functions internally and have you have to pass a value inside brackets which internally return the same value.

Chrome is lenient in allowing budgetlineviewmodel.total() to tolerate null value when no parameter is passed.

hope this helps.

Cheers !

budgetlineviewmodel.Save = function () {        
        var currentTotalAmt = $('#txtTotal').val();
        budgetLineViewModel.Total(currentTotalAmt);
}
Grigson answered 5/5, 2015 at 12:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.